push_back对象进入向量内存问题C ++

时间:2013-09-25 02:47:37

标签: c++ memory vector

比较这两种初始化对象矢量的方法。

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复制被推送的值吗?我的问题是,我有很大的对象和很长的向量,所以我需要找到一种最好的方法来节省内存。

  • 第二种方式更好吗?
  • 是否有其他方法可以获得对象/指针的向量,以后我可以找到每个对象并同时使用最少的内存?

4 个答案:

答案 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 *>想法。