如何实现指向其他乘型向量的C ++向量? [英] How can implement a C++ vector that points to other, multiply typed vectors?

查看:83
本文介绍了如何实现指向其他乘型向量的C ++向量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to store elements of multiple types in a single vector, while keeping elements of the same type contiguous. The types are derived from a base class and I expect different types to be implemented throughout the development cycle. For this reason it would help if the process of adding a new type to the list is very straightforward.

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

I can achieve this (to an extent) in the following manner:

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

但这不是干净的,易于维护的,也不是易于编译的(保存数据的类包含在很多地方).

However this is not clean, easy to maintain, nor compile-friendly (the class holding the data is included in many places).

我认为一个更易于编译(但更丑陋)的选项

A more compile friendly (but uglier) option I thought was doing something like this

//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!)

有什么可以像这样正常工作的吗?

Is there anything that would work generically like this?

vector< vector<?>* > vectorOfVariedVectors;

此结构的动机是将组件存储在Entity-Component设计模式中.

The motivation for this structure is to store components in the Entity-Component design pattern.

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

The reason I want the Types (or rather, components) to be contiguous, is to be able to transverse them in a cache-friendly way. This means that I want the instances themselves to be contiguous. While using contiguous pointers would give me a behaviour similar to what I want, if they point to 'random' places in memory the cache misses will still happen when fetching their data.

避免内存碎片是这样做的另一个好处.

Avoiding memory fragmentation is a nice additional benefit of this.

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

The main idea is to have a clean manager type class that holds and provides access to these elements. Having multiple member vectors that other developers would have to add to this class is undesirable as long as this requires users that create new classes to alter this manager class. Edits to this container class should be as simple as possible or hopefully, non-existent.

找到解决方案

感谢德米特里·列登佐夫(Dmitry Ledentsov)将我指向本文.这几乎就是我想要的.

Thanks to Dmitry Ledentsov for pointing me to this article. This is pretty much what I was looking for.

推荐答案

正如其他人在评论中已经指出的那样,与您要寻找的容器相比,在更大范围内可能有更好的解决方案.无论如何,这就是您可以按照自己的意愿去做的事情.

As others have already pointed out in the comments, there is probably a better solution to your problem at a larger scale than the container you are looking for. Anyway, here is how you could do what you are asking for.

基本思想是将BaseTypestd::unique_ptrstd::vector个存储在以std::type_index es为键的std::map中.该示例使用C ++ 11功能.为简洁起见,省略了运行时错误处理.

The basic idea is to store std::vectors of std::unique_ptrs of BaseTypes in a std::map with std::type_indexes as keys. The example uses C++11 features. Run-time error handling is omitted for brevity.

首先,一些标题:

#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

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

Next, let us define the class hierarchy. I will define an abstract base class and a template to make as many derived types as needed. It should be clear that the container works equally well for any other class hierarchy.

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无需进行任何更改.

Note that we have not even declared the possible sub-types of BaseType so far. That is, PolyContainer does not need to be altered in any way if a new sub-type is added.

我已经将lookup制成了模板化函数,因为我发现它更加简洁.如果您不想这样做,显然可以添加一个附加的std::type_info参数,然后使用lookup(typid(SubType), 42)而不是lookup<SubType>(42).

I have made lookup a templated function because I find it much cleaner. If you don't want to do this, you can obviously add an additional std::type_info parameter and then use lookup(typid(SubType), 42) instead of lookup<SubType>(42).

最后,让我们使用已有的东西.

Finally, let's use what we have.

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

这篇关于如何实现指向其他乘型向量的C ++向量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆