如何实现指向其他多重类型向量的C ++向量?

时间:2014-11-26 15:58:27

标签: c++ vector generic-programming contiguous

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

我可以通过以下方式实现这一目标:

//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];
    }
}

(EW!)

有没有像这样一般工作的东西?

vector< vector<?>* > vectorOfVariedVectors;

修改

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

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

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

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

找到解决方案

感谢Dmitry Ledentsov指点我this article。这几乎就是我想要的。

1 个答案:

答案 0 :(得分:1)

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

基本思路是将std::vector std::unique_ptrBaseType的{​​{1}}个std::map存储在std::type_index#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 作为键。该示例使用C ++ 11功能。为简洁起见,省略了运行时错误处理。

首先,一些标题:

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; }

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

{{1}}