2

这里比较两种初始化对象向量的方式。

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

4 回答 4

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,并且对象确实很大,那么“指向数据的指针向量”是可以接受的。

无论如何,您真正应该做的是确定哪个与您的模型最匹配,检查性能,并且只有在存在实际性能问题时才进行优化。

于 2013-09-25T03:12:39.153 回答
1

如果您的Obj类不需要多态行为,那么最好将Obj类型直接存储在vector<Obj>.

如果您将对象存储在 中vector<Obj*>,那么您将承担在不再需要这些对象时手动解除分配这些对象的责任。在这种情况下,最好vector<std::unique_ptr<Obj>>尽可能使用,但同样,只有在需要多态行为时才使用。

vector会将对象存储在堆上Obj(默认情况下,除非您allocatorvector模板中覆盖)。这些对象将存储在连续的内存中,这也可以为您提供更好的缓存位置,具体取决于您的用例。

使用的缺点vector<Obj>是频繁插入/删除vector可能会导致Obj对象的重新分配和复制。但是,这通常不会成为您的应用程序的瓶颈,如果您觉得是,您应该对其进行分析。

使用 C++11移动语义,可以大大减少复制的影响。

于 2013-09-25T02:53:21.680 回答
1

vector<Obj>如果您可以提前保留大小,则 使用 a将占用更少的内存来存储。vector<Obj *>与不必重新分配向量相比,必然会使用更多vector<Obj>的内存,因为您有指针的开销和动态内存分配的开销。如果您只有几个大对象,则此开销可能相对较小。

但是,如果您非常接近内存不足,vector<Obj>如果您无法提前保留正确的大小,则使用可能会导致问题,因为在重新分配向量时您将暂时需要额外的存储空间。

拥有大量大型对象的向量也可能导致内存碎片问题。如果您可以在程序执行的早期创建向量并保留大小,这可能不是问题,但如果稍后创建向量,您可能会因为堆上的内存漏洞而遇到问题。

于 2013-09-25T02:57:16.333 回答
0

在这种情况下,我会考虑第三种可能性:使用std::deque而不是std::vector.

这是您给出的两者之间的中间点。Avector<obj>分配一个巨大的块来保存向量中对象的所有实例。Avector<obj *>分配一个指针块,但对象的每个实例都在它自己的块中。因此,您得到 N 个对象和 N 个指针。

一个双端队列将创建一个指针块和许多对象块——但是(至少通常情况下)它将许多对象(称为 M)放在一个块中,所以你得到一个 N/ 块M 指针和 N/M 个对象。

这避免了对象向量或指针向量的许多缺点。一旦分配了一个对象块,就不必重新分配或复制它们。您确实(或可能)最终必须重新分配指针块,但如果您尝试手动分配,它将比指针向量小(M 倍)。

一个警告:如果您使用的是 Microsoft 的编译器/标准库,这可能效果不佳——它们有一些奇怪的逻辑(在 VS 2013 RC 中仍然存在),这意味着如果您的对象大小大于 16,您将每个块只得到一个对象——即,相当于你的vector<obj *>想法。

于 2013-09-25T03:47:42.470 回答