比较这两种初始化对象矢量的方法。
1.
vector<Obj> someVector;
Obj new_obj;
someVector.push_back(new_obj);
2.
vector<Obj*> ptrVector;
Obj* objptr = new Obj();
ptrVector.push_back(objptr);
第一个push_back实际对象而不是对象的指针。矢量push_back复制被推送的值吗?我的问题是,我有很大的对象和很长的向量,所以我需要找到一种最好的方法来节省内存。
答案 0 :(得分:2)
在上述两个选项中,第三个选项中没有一个是效率最高的选项:
std::vector<Obj> someVector;
someVector.reserve(preCalculatedSize);
for (int i = 0; i < preCalculatedSize; ++i)
someVector.emplace_back();
emplace_back
直接将对象构造到vector
安排它的内存中。如果您在使用前reserve
,则可以避免重新分配和移动。
但是,如果对象确实很大,那么缓存一致性的优点就更少了。因此,vector
智能指针是有道理的。因此第四个选项:
std::vector< std::unique_ptr<Obj> > someVector;
std::unique_ptr<Obj> element( new Obj );
someVector.push_back( std::move(element) );
可能是最好的。在这里,我们代表数据的生命周期以及如何在相同的结构中访问它,几乎没有开销,防止它不同步。
当您要移动std::move
时,您必须明确std::unique_ptr
。如果由于某种原因需要原始指针,.get()
是如何访问它。 ->
以及*
和explicit operator bool
都被覆盖,因此当您的界面需要.get()
时,您只需要调用Obj*
。
这两种解决方案都需要C ++ 11。如果你缺少C ++ 11,并且对象真的很大,那么“数据指针的向量”是可以接受的。
在任何情况下,您真正应该做的是确定哪个模型最匹配,检查性能,并且只有在存在实际性能问题时才进行优化。
答案 1 :(得分:1)
如果您的Obj
课程不需要多态行为,那么最好只将Obj
类型直接存储在vector<Obj>
中。
如果您将对象存储在vector<Obj*>
中,那么您将负责在不再需要这些对象时手动取消分配这些对象。在这种情况下,更好的是,如果可能的话,使用vector<std::unique_ptr<Obj>>
,但同样,只有在需要多态行为的情况下才能使用。{/ p>
vector
会将Obj
个对象存储在堆上(默认情况下,除非您覆盖allocator
模板中的vector
)。这些对象将存储在连续的内存中,这也可以根据您的使用情况为您提供更好的缓存位置。
使用vector<Obj>
的缺点是频繁插入/删除vector
可能会导致重新分配和复制Obj
个对象。但是,这通常不会成为您应用程序的瓶颈,如果您愿意,应该对其进行分析。
使用C ++ 11 move semantics,复制的含义可以大大减少。
答案 2 :(得分:1)
如果您可以提前预留大小,使用vector<Obj>
将占用更少的内存。如果不必重新分配向量,vector<Obj *>
必然会使用比vector<Obj>
更多的内存,因为你有指针的开销和动态内存分配的开销。如果你只有一些大型物体,这个开销可能相对较小。
但是,如果您非常接近内存不足,如果您无法提前保留正确的大小,则使用vector<Obj>
可能会导致问题,因为在重新分配矢量时您将暂时需要额外的存储空间
拥有大型对象的大型向量也可能导致内存碎片问题。如果你可以在程序执行的早期创建向量并保留大小,这可能不是问题,但是如果稍后创建向量,则可能由于堆上的内存漏洞而遇到问题。
答案 3 :(得分:0)
在这种情况下,我会考虑第三种可能性:使用std::deque
代替std::vector
。
这是你给出的两个之间的中间点。 vector<obj>
分配一个巨大的块来保存向量中对象的所有实例。 vector<obj *>
分配一个指针块,但是它自己的块中的每个对象实例。因此,你得到N个对象和N个指针。
deque会创建一个指针块和一些对象块 - 但是(至少通常)它会将一些对象(称为M)放在一起组成一个块,所以你得到一个块N / M指针和N / M对象。
这避免了对象向量或指针向量的许多缺点。一旦分配了一个对象块,就不必重新分配或复制它们。你做(或可能)最终必须重新分配指针块,但是如果你试图手工做它,它会比指针向量小(比M倍)。
有一点需要注意:如果您使用的是Microsoft的编译器/标准库,这可能效果不佳 - 它们有一些strange logic(仍然通过VS 2013 RC出现),这意味着如果您的对象大小更大如果超过16,则每个块只能获得一个对象 - 即相当于您的vector<obj *>
想法。