2013-09-25 28 views
2

比較兩種初始化對象向量的方法。將push_back對象轉換爲向量內存問題C++

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是否正在複製正在推送的值?我的問題是,我有巨大的對象和很長的矢量,所以我需要找到一種節省內存的最佳方式。

  • 第二種方式更好嗎?
  • 是否有其他方法可以獲得對象/指針的向量,以便以後可以找到每個對象並同時使用最少的內存?
+1

如果可以的話,它會在C++ 11中移動它,並且第一個試圖推送一個函數。 – chris

+0

@chris我需要在代碼中明確寫出讓它移動但不復制的東西?或者使用C++ 11指定編譯?或者我不必寫任何它只是爲我做的?那裏的第一個我是一個構造函數。 –

+0

我很好奇;如何「巨大」是你的班級的一個實例? –

回答

1

上述兩種選擇中,該第三不包含一個是最有效的:

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::movestd::unique_ptr。如果您因任何原因需要原始指針,.get()是如何訪問它。 ->*explicit operator bool全部被覆蓋,所以當你有一個接口需要Obj*時,你只需要打電話.get()

這兩個解決方案都需要C++ 11。如果你缺少C++ 11,並且對象真的很大,那麼「指向數據的向量」是可以接受的。

無論如何,你真正應該做的就是確定哪個最適合你的模型,檢查性能,並且只有在存在實際性能問題時才進行優化。

+0

我會建議「向數據的智能指針矢量」,而不是「指向數據的向量」 –

1

如果您Obj類不需要多態行爲,那麼最好是簡單地直接存儲Obj類型的vector<Obj>

如果您將對象存儲在vector<Obj*>中,那麼您將負責在不再需要這些對象時手動釋放這些對象。在這種情況下,如果可能的話,最好使用vector<std::unique_ptr<Obj>>,但是,只有在需要多態行爲時纔是如此。

vector將在堆上存儲Obj對象(默認情況下,除非您覆蓋vector模板中的allocator)。這些對象將存儲在連續的內存中,這也可以爲您提供更好的緩存局部性,具體取決於您的用例。

使用vector<Obj>的缺點是頻繁插入/從vector中刪除可能導致您的對象重新分配和複製。但是,這通常不會成爲應用程序中的瓶頸,如果您覺得這樣,您應該對其進行配置。

使用C++ 11 move semantics,可以大大減少複製的影響。

+1

我認爲OP是擔心副本本身。這裏的任何答案都應該提到移動語義和事實,如果使用指針,你現在必須自己釋放每個元素。 –

1

如果您可以提前預訂尺寸,則使用vector<Obj>將佔用較少的內存。 vector<Obj *>將必然使用比vector<Obj>更多的內存,如果該向量不必重新分配,因爲您有指針的開銷和動態內存分配的開銷。儘管如果你只有幾個大對象,這個開銷可能相對較小。

但是,如果您非常接近內存不足的空間,如果您無法提前保留正確的大小,則使用vector<Obj>可能會導致問題,因爲在重新分配矢量時您暫時需要額外的存儲空間。

擁有大對象的大型矢量也可能導致內存碎片問題。如果您可以在執行程序時儘早創建矢量並保留大小,這可能不是問題,但如果稍後創建矢量,則可能會由於堆中的內存空洞而遇到問題。

+0

然後,我會堅持第一個選擇。謝謝! –

0

在這種情況下,我會考慮第三種可能性:使用std::deque而不是std::vector

這是你給出的兩個中間點。 A vector<obj>分配一個巨大的塊來保存矢量中所有對象的實例。 A vector<obj *>分配一個指針塊,但是它自己的一個塊中的每個對象實例。因此,你得到N個對象加上N個指針。

一個deque將創建一個指針塊和一些對象塊 - 但是(至少通常)它會將一些對象(稱爲M)一起放到一個塊中,所以你得到一個N/M指針塊和N/M個對象。

這避免了許多對象矢量或指針矢量的缺點。一旦分配了一塊對象,就不必重新分配或複製它們。你確實(或可能)最終不得不重新分配指針塊,但是如果你嘗試手工完成,它會比指針矢量更小(比M小)。需要注意的一點是,如果你使用的是微軟的編譯器/標準庫,這可能不太適用 - 它們有一些strange logic(仍然存在於VS 2013 RC中),這意味着如果你的對象大小大於16,你會得到每個塊只有一個對象 - 即相當於你的想法vector<obj *>