2012-03-23 77 views
4

假設你有一個類的std :: list。有兩種方法可以讓這個列表: 1)std ::對象列表效率

std::list<MyClass> myClassList; 
MyClass myClass; 
myClassList.push_front(myClass); 

使用這種方法,當你傳遞對象到列表中的拷貝構造函數將會被調用。如果這個類有許多成員變量,並且你多次進行這個調用,它可能會變得昂貴。

2)

std::list<MyClass*> myClassList; 
MyClass* myClass = new MyClass(); 
myClassList.push_front(myClass); 

此方法不會呼籲類的拷貝構造函數。我不完全肯定在這種情況下會發生什麼,但我認爲該列表將創建一個新的MyClass *並分配參數的地址。事實上,如果你在棧上而不是堆上創建myClass,並讓它超出範圍,那麼myClassList.front()是無效的,所以必須是這種情況。

如果我對此有誤,請與我反駁,但我相信第二種方法對於某些類更有效率。

+0

你說得對。 – 2012-03-23 05:10:06

+3

我個人會在那裏有一個'unique_ptr '如果你想讓ptrs被釋放。 – 2012-03-23 05:11:30

+7

還有第三種方式,它比這兩個方法更有效率:'myClassList.emplace_front()'。另外 - 如果效率是一個問題,你幾乎肯定不想使用'std :: list'。 – Mankarse 2012-03-23 05:12:08

回答

2

這總是一個問題。

首先,它確實取決於您的編譯器是否支持C++ 11移動語義,因爲這會顯着改變問題的各個方面。

對於那些被困在C++ 03

有多種選擇:

std::list<MyClass> list; 
list.push_front(MyClass()); 

即使在語義上有一個副本,優化程序可能去除大部分冗餘/死專賣店。大多數優化器將要求缺省構造函數和複製構造函數的定義可用。

boost::ptr_deque<MyClass> deque; 
std::auto_ptr<MyClass> p(new MyClass()); 
deque.push_front(p); 

ptr_vector可用於則應更換push_frontpush_back,否則就有點浪費。這避免了std::list<MyClass*>的大部分內存開銷,並具有自動處理內存的額外好處。

boost::stable_vector<MyClass> svec; 
svec.push_back(MyClass()); 
//  ~~~~ 

有一個副本(如表),但沒有進一步的副本應在容器內進行(如表)的保證。它還允許一些比列表更多的操作(例如,隨機訪問),代價是對於大型容器而言插入的速度較慢。

對於那些享有C++ 11

std::list<MyClass> list; 
list.push_front(MyClass()); 

不產生任何副本,而不是發生移動操作。

也可以用附帶構建地方對象的新操作:

std::list<MyClass> list; 
list.emplace_front(); 

將直接節點,不復制,不移動中創建一個新的MyClass

最後,你不妨在容器上的更緊湊的表示或其他操作,在這種情況下:

std::vector<std::unique_ptr<MyClass>> vec; 
vec.emplace_back(new MyClass()); 

爲您提供隨機訪問和更低的內存開銷。

3

這裏要考慮的重要一點比性能問題要微妙得多。

標準庫容器工作於複製語義,它們創建了您添加到容器的元素的副本。
一般來說,除非您絕對需要,否則遠離C++中的動態內存分配總是更好。第一種選擇是更好的,因爲您不必擔心內存分配和釋放,容器將獲得您添加到其中的對象的所有權,併爲您執行管理。

在第二種情況下,容器不會取得您添加的元素的所有權,您必須自己管理它。如果你一定要用智能指針作爲容器元素而不是原始指針。

關於性能,您將需要在系統上對代碼示例進行分析,以查看性能差異是否足夠顯着以選擇一種方法。

+0

因此,爲什麼我建議unique_ptr,雖然這就是C++ 11(但我使用的所有編譯器都支持:)) – 2012-03-23 05:14:08

+0

另一種可能性是使用[專門用於指針的容器](http://www.boost.org/ DOC /庫/ 1_49_0 /庫/ ptr_container/DOC/ptr_container.html)。 – 2012-03-23 05:18:32

+0

@JerryCoffin:它看起來像一箇舊式的unique_ptr,但是一個容器而不是一個類型。我沒有檢查確定 – 2012-03-23 05:21:53

0

第一種方法的問題 - 當MyClass很大並且無法在兩個數據結構中(兩個列表中的每個具有不同的語義;在列表和樹中)中具有相同的對象時,性能較差。如果這些不利因素不會影響到您,請使用第一種方法。

第二種方法更高效,但可能難以管理。例如,如果對象不再可用,則需要正確銷燬MyClass對象。在有例外的情況下,這可能不是微不足道的(閱讀有關C++ exception safety)。我建議你看看Boost Smart Pointers,它打算緩解C++指針管理。 C++ 11具有這些內置的功能,因此如果使用現代編譯器,則不需要Boost。閱讀Wikipedia作簡短介紹。

2

如果你真的關心性能,但仍然需要使用鏈表,請考慮使用boost::intrusive::list。使用std::list時遇到的主要問題是您需要從堆中分配新內存,即使是大多數情況下的複製構建,這可能也會更加昂貴。由於boost::intrusive::list將分配給您,因此您可以將對象保留在std::vector中並分批分配。這樣,您還可以獲得更好的緩存局部性,這是性能的另一個關注點。或者,您可以使用std :: list的自定義分配器來執行相同的操作。由於使用自定義分配器std::list可能與使用boost插入列表一樣混亂,因此我會隨着boost的推出,因爲您會獲得許多其他有用的功能(例如在多個列表中保留相同的對象等)。編譯器可能會優化掉任何不必要的拷貝(根據你使用它的方式是不必要的)。

+0

感謝帖子傢伙,很多很好的答案。 Enobayram是唯一一個涉及緩存局部性的問題,這也是優化容器的主要因素。我想唯一能找到最佳解決方案的方法是稍後再分析我的代碼。 – 2012-03-24 00:08:58