【问题标题】:How can implement a C++ vector that points to other, multiply typed vectors?如何实现指向其他多类型向量的 C++ 向量?
【发布时间】:2015-01-25 01:00:30
【问题描述】:

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

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

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

【问题讨论】:

  • TypeA、TypeB 和 TypeC 类是你实现的吗?
  • 您需要重新考虑您的设计。多态性的重点是您不需要知道确切的类型,那么为什么要让它们连续呢?
  • 我想在一个向量中存储多种类型的元素 - 为什么?这个功能应该如何使用?
  • 为什么每种类型没有一个向量?
  • 查看这篇文章:1

标签: c++ vector generic-programming contiguous


【解决方案1】:

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

基本思想是将std::vectors of std::unique_ptrs of BaseTypes 存储在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&lt;SubType&gt;(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;
}

【讨论】:

  • 我喜欢您在这里使用模板。这让我知道如何组织不同类型的课程。我在这里看到的主要问题是这些类实际上并不连续,因为它们是用 new 创建的。编辑帖子以获取更多信息。
  • 如果不在免费存储中创建对象(即使用new),运行时多态是很困难的。您必须将容器设为模板,并让用户明确知道要存储在其中的所有类型(这基本上让它成为std::tuple&lt;std::vector&lt;TypeA&gt;, std::vector&lt;TypeB&gt;, ...&gt;)或使用boost::any 之类的技巧。
  • 只需阅读 Dmitry Ledentsov 链接的文章即可。这确实是一种有趣的第三种方法。我没想到。
猜你喜欢
  • 2023-03-23
  • 2013-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-28
  • 1970-01-01
  • 2019-02-26
  • 2013-11-21
相关资源
最近更新 更多