0

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

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

//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 将我指向这篇文章。这几乎就是我想要的。

4

1 回答 1

1

正如其他人已经在评论中指出的那样,对于您的问题,可能有比您正在寻找的容器更好的解决方案。无论如何,这就是你如何做你所要求的。

基本思想是将std::vectors of std::unique_ptrs of BaseTypes 存储在 a 中std::map,其中std::type_indexes 作为键。该示例使用 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;
}
于 2014-11-26T17:25:09.620 回答