我想在单个向量中存储多种类型的元素,同时保持相同类型的元素连续。类型派生自基类,我期望在整个开发周期中实现不同的类型。因此,如果向列表添加新类型的过程非常简单,那将会有所帮助。
我可以通过以下方式实现这一目标:
//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。这几乎就是我想要的。
答案 0 :(得分:1)
正如其他人已经在评论中指出的那样,对于您的问题,可能会有比您正在寻找的容器更大的解决方案。无论如何,这就是你如何做你想要的。
基本思路是将std::vector
std::unique_ptr
个BaseType
的{{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}}