如何实现指向其他乘法类型向量的C++向量

How can implement a C++ vector that points to other, multiply typed vectors?

本文关键字:向量 类型 其他 C++ 何实现 实现      更新时间:2023-10-16

我想将多种类型的元素存储在单个向量中,同时保持相同类型的元素连续。这些类型派生自基类,我希望在整个开发周期中实现不同的类型。因此,如果将新类型添加到列表的过程非常简单,这将有所帮助。

我可以通过以下方式(在一定程度上)实现这一点:

//header
enum TypeID { TypeA_ID, TypeA_ID, TypeA_ID, TypeIDAmount };
vector<TypeA> vectorA;
vector<TypeB> vectorB;
vector<TypeC> vectorC;
//cpp
TypeBase* LookUp(TypeID type, int index)
{
    switch(type)
    {
    case TypeA_ID: return (TypeBase*) &vectorA[index];
    case TypeB_ID: return (TypeBase*) &vectorB[index];
    case TypeC_ID: return (TypeBase*) &vectorC[index];
    }
}

然而,这并不干净,易于维护,也不易于编译(保存数据的类包含在许多地方)。

一个更编译友好(但更丑陋)的选项,我认为正在做这样的事情

//header
void* vectorArray;
//cpp
void Initialize()
{
    vectorArray = new void*[TypeIDAmount];
    vectorArray[0] = new vector<TypeA>;
    vectorArray[1] = new vector<TypeB>;
    vectorArray[2] = new vector<TypeC>;
}
TypeBase* LookUp(TypeID type, int index)
{
    void* pTypedVector = &vectorArray[type];
    switch(type)
    {
    case TypeA_ID: return (TypeBase*) (*(vector<TypeA>*)pTypedVector)[index];
    case TypeB_ID: return (TypeBase*) (*(vector<TypeB>*)pTypedVector)[index];
    case TypeC_ID: return (TypeBase*) (*(vector<TypeC>*)pTypedVector)[index];
    }
}

(嘭!

有什么东西可以像这样工作吗?

vector< vector<?>* > vectorOfVariedVectors;

编辑:

此结构的动机是将组件存储在实体组件设计模式中。

我希望类型(或者更确切地说,组件)是连续的,是能够以缓存友好的方式横向它们。这意味着我希望实例本身是连续的。虽然使用连续指针会给我一种类似于我想要的行为,但如果它们指向内存中的"随机"位置,则在获取数据时仍然会发生缓存未命中。

避免内存碎片是这样做的一个很好的额外好处。

主要思想是拥有一个干净的管理器类型类来保存并提供对这些元素的访问。具有其他开发人员必须添加到此类的多个成员向量是不可取的,只要这需要创建新类的用户更改此管理器类。对此容器类的编辑应尽可能简单,或者希望不存在。

找到的解决方案

感谢德米特里·列登佐夫(Dmitry Ledentsov)为我指出这篇文章。这几乎就是我想要的。

正如其他人在评论中已经指出的那样,可能有一个比您正在寻找的容器更大规模的问题解决方案。 无论如何,这是您可以完成您要求的事情的方法。

基本思想是将std::unique_ptr s 的 BaseType s 的 std::vector 存储在一个std::map中,std::type_index es 作为键。 该示例使用 C++11 要素。 为简洁起见,省略了运行时错误处理。

首先,一些标头:

#include <cstddef>      // std::size_t
#include <iostream>     // std::cout, std::endl
#include <map>          // std::map
#include <memory>       // std::unique_ptr
#include <sstream>      // std::ostringstream
#include <string>       // std::string
#include <type_traits>  // std::enable_if, std::is_base_of
#include <typeindex>    // std::type_index
#include <typeinfo>     // typid, std::type_info
#include <utility>      // std::move
#include <vector>       // std::vector

接下来,让我们定义类层次结构。 我将定义一个抽象基类和一个模板,以根据需要创建任意数量的派生类型。 应该清楚的是,容器同样适用于任何其他类层次结构。

class BaseType
{
public:
  virtual ~BaseType() noexcept = default;
  virtual std::string
  name() const = 0;
};
template<char C>
class Type : public BaseType
{
private:
  const std::string name_;
public:
  Type(const std::string& name) : name_ {name}
  {
  }
  virtual std::string
  name() const final override
  {
    std::ostringstream oss {};
    oss << "Type" << C << "(" << this->name_ << ") @" << this;
    return oss.str();
  }
};

现在进入实际容器。

class PolyContainer final
{
private:
  std::map<std::type_index, std::vector<std::unique_ptr<BaseType>>> items_ {};
public:
  void
  insert(std::unique_ptr<BaseType>&& item_uptr)
  {
    const std::type_index key {typeid(*item_uptr.get())};
    this->items_[key].push_back(std::move(item_uptr));
  }
  template<typename T,
           typename = typename std::enable_if<std::is_base_of<BaseType, T>::value>::type>
  BaseType&
  lookup(const std::size_t i)
  {
    const std::type_index key {typeid(T)};
    return *this->items_[key].at(i).get();
  }
};

请注意,到目前为止,我们甚至还没有声明可能的BaseType子类型。 也就是说,如果添加了新的子类型,则不需要以任何方式更改PolyContainer

lookup做了一个模板化函数,因为我发现它更干净。 如果您不想这样做,显然可以添加一个额外的std::type_info参数,然后使用 lookup(typid(SubType), 42) 而不是 lookup<SubType>(42)

最后,让我们使用我们所拥有的。

using TypeA = Type<'A'>;
using TypeB = Type<'B'>;
using TypeC = Type<'C'>;
// As many more as you like...
int
main()
{
  PolyContainer pc {};
  pc.insert(std::unique_ptr<BaseType> {new TypeA {"first"}});
  pc.insert(std::unique_ptr<BaseType> {new TypeA {"second"}});
  pc.insert(std::unique_ptr<BaseType> {new TypeB {"third"}});
  pc.insert(std::unique_ptr<BaseType> {new TypeC {"fourth"}});
  pc.insert(std::unique_ptr<BaseType> {new TypeB {"fifth"}});
  std::cout << pc.lookup<TypeB>(0).name() << std::endl;
  std::cout << pc.lookup<TypeB>(1).name() << std::endl;
  return 0;
}