类型仅在运行时已知的同类容器
Homogeneous container where type is known only at runtime
我有一个单类型集合,其类型仅在运行时已知。定义类型后,它将永远不会更改。我目前正在向量中存储指向对象的指针,如下所示:
std::vector<Animal*> v;
我想知道是否可以将实例存储在连续内存中。我的目的是编写一个对缓存更友好的代码,并更快地遍历容器。
我可以为每个向量的元素使用 boost::variant ,例如,
std::vector<boost::variant< Cat, Dog > >
但是,如果sizeof(Dog)
比sizeof(Cat)
大得多,那么在对象类型为 Cat
的情况下,内存就会浪费
我还可以使用容器的变体:
boost::variant< std::vector<Cat>, std::vector<Dog> >
但我不知道在这种情况下迭代器会如何,以及它们是否会引入更多的开销。
"指针向量方法"是我们能做的最好的事情吗?
更多信息:对象的大小在 50 到 250 字节之间,容器长度在 10K 到 1M 元素之间,我必须遍历容器一百万次。
谢谢。
编辑:我在这里发现了一个类似的问题(也有很好的建议(:如何在C++中编写缓存友好的多态代码?
我要把我的评论变成一个答案。
我想说你最好的选择是把所有的狗放进一个vector<Dog>
,把所有的猫放进一个vector<Cat>
,然后分别迭代它们。这样,您就可以保持每个载体的最佳包装。
使用一些CRTP,您可以将其自动化,以便轻松添加更多动物而不会遇到麻烦。
一些例子:
template <typename T>
class Container{
public:
static std::vector<T> m_elements; //static vector will contain animals
//overloaded operator new adds the Animal to m_elements
void* operator new(size_t){
m_elements.push_back( T{} );
return &m_elements[m_elements.size() - 1];
}
};
template <typename T> std::vector<T> Container<T>::m_elements;
//some example animals
class Dog : public Container<Dog>{
public:
std::string woof;
Dog( char* s = "woof" ){
woof = s;
}
};
class Cat : public Container<Cat>{
public:
std::string meow;
Cat( char* s = "meow" ){
meow = s;
}
};
int main(){
new Dog( "woof" );
new Dog( "rrawoof" );
new Cat( "meow" );
new Cat( "meweeow" );
//easy iteration
for( auto dog : Dog::m_elements )
std::cout << dog.woof << "n";
for( auto cat : Cat::m_elements )
std::cout << cat.meow << "n";
std::cout << "end";
}
超载new
是否是一个好主意是一个不同的问题,但它对展示来说很好。
对 - 在这里完全重写,而且更简单。
我同意 s3rius 的观点,你仍然应该使用 std::vector。理想情况下,如果您要存放猫,您会使用...
std::vector<Cat>
如果你正在存放狗,你会想要...
std::vector<Dog>
但是,您需要运行时多态性来选择正在处理的情况。
一种方法是(或受其启发(策略设计模式。为这些向量的接口定义基类,并有一个模板类实现包含该向量的接口。
class Animals_IF
{
public:
virtual int size () const = 0;
};
template<typename T> class Animals_Vector
{
private:
std::vector<T> store;
public:
int size () const;
};
template<typename T> int Animals_Vector<T>::size () const
{
return store.size ();
}
这里的问题是界面不能提及Cat
或Dog
,因为它不知道具体类型,这当然是我选择size
作为上面示例方法的原因。
一种解决方案是使用可能类型的boost::variant
传递值,因此每个策略/包装类都可以在使用它们之前检查它获得的值是否正确。变体中的包装/解包值可以由(非模板(基类中的模板方法处理。
在所有包装和解包效率低下的情况下,您必须确定您正在处理的情况,然后通过正确的策略/包装器类型(而不是基类(调用。为此,请对所有策略/包装器案例进行提升::变体。这并不妨碍您还拥有指向基类的指针。实际上,将指向基类的指针和boost::variant
都包装在一个类中(在需要时使用模板方法(。
class Animals_IF
{
public:
typedef boost::variant<Cat,Dog> Animal;
virtual int size () const = 0;
template<typename T> void slow_push (const T &p)
{
push_ (Animal (p));
}
private:
virtual void slow_push_ (const Animal &p) = 0;
};
template<typename T> class Animals_Vector
{
public:
int size () const;
void fast_push (const T &p);
private:
std::vector<T> store;
void slow_push_ (const Animal &p);
};
template<typename T> int Animals_Vector<T>::size () const
{
return store.size ();
}
template<typename T> void Animals_Vector<T>::fast_push (const T &p)
{
store.push (p);
}
template<typename T> void Animals_Vector<T>::slow_push_ (const Animal &p)
{
const T* item = boost::get<T> (&p);
if (T) store.push (*item);
// else throw?
}
class Animals
{
public:
int size () const
{
// null check needed?
return ptr->size ();
}
template<typename T> void slow_push (const T &p)
{
// null check needed?
ptr->slow_push (p);
}
template<typename T> void fast_push (const T &p)
{
Animals_Vector<T> *lptr = boost::get<T> (&store);
if (lptr) lptr->fast_push (p);
// else throw?
}
private:
Animals_IF* ptr;
boost::variant<Animals_Vector<Cat>,Animals_Vector<Dog>> store;
};
如果共享接口无法真正提供任何东西(因为每个方法都需要传递值,并且包装/解包为变体是不可接受的(,那么整个策略的事情是不必要的。只需对不同的 std::vector 类型进行提升::变体。
此外,上面的fast_push
不会很快,因为push
太简单而无法受益 - 这个想法是,该方法对于复杂的方法来说更快,可以通过预先完成一次来避免重复的运行时类型检查。
顺便说一句 - 好问题。
- CMake-按正确顺序将项目与C运行时对象文件链接
- 我在c++代码中生成了一个运行时#3异常
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 删除指向指针的指针是运行时错误吗
- 如何用参数值调用函数(仅在运行时已知)
- 为什么即使使用-cudart-static进行编译,库用户仍然需要链接到cuda运行时
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- c++中的指针和运行时错误
- 在运行时处理类型擦除的数据-如何不重新发明轮子
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 建议在运行时将带有类实例的列表从c++导入qml
- 无法理解此 return 语句的功能,没有它就会发生运行时错误
- 如何在GTK程序运行时禁用屏幕保护程序/电源管理/屏幕消隐
- 在同一模拟中使用静脉和静脉_ inet内容时出现运行时错误
- 读取文件时运行时的未知行为
- 函数在Windows或Linux上运行时表现不同
- 在声明中合并两个常量"std::set"(不是在运行时)
- AWS Lambda C++运行时权限被拒绝
- 两个不同类的运行时多态性
- 类型仅在运行时已知的同类容器