2010-04-05 69 views
22

我有一個C++程序使用包含類實例的std :: list。如果我打電話給它經歷了創建一個臨時變量的過程,然後立即將它複製到該向量,然後刪除該臨時變量。這並不像我想要的那麼高效,而且當您需要深度複製時會很糟糕。如何在不調用複製構造函數的情況下使用類初始化STL向量/列表

我很想有我的類new的構造函數的東西,而不必實現一個拷貝構造函數只是爲了第二次分配我的內存並浪費運行時。我寧願不必立即從vector/list中找到類實例,然後手動分配內存(或者做一些可怕的事情,比如在拷貝構造函數中分配內存)。

有沒有辦法解決這個問題(我沒有使用Visual Studio BTW)?

+1

你使用什麼編譯器?我相信這種情況通常會在較新的編譯器中進行優化。 – 2010-04-05 18:27:48

+0

您是否使用優化版本進行分析? – 2010-04-05 18:33:25

+0

您需要使用發佈版本進行測試。它可能在DEBUG模式下執行此操作,但在釋放模式下使用RVO(返回值優化),從而消除了副本。 – Nate 2010-04-05 18:42:11

回答

9

C++ 0x移動構造函數是一個部分解決方法:而不是被調用的複製構造函數,移動構造函數會是。移動構造函數就像複製構造函數,除了它允許使源參數失效。

C++ 0x增加另一個功能,它會做你想要的:emplace_back。 (N3092§23.2。3)將參數傳遞給構造函數,然後用參數(由...和轉發)調用構造函數,所以不能調用其他構造函數。

至於C++ 03,你唯一的選擇是給你的類添加一個未初始化的狀態。在push_back之後立即調用另一個函數執行實際構造。 boost::optional可能會幫助你避免初始化班級的成員,但它又需要他們是可複製構建的。或者,正如弗雷德所說,用最初空白的智能指針來完成同樣的事情。

+0

這就是我要找的東西。我有一個支持C++ 0x的編譯器,就像std :: lists的便利,沒有大量指針的內存/堆分配缺陷。有趣的是,我從來沒有聽說過emplace_back,這是我剛剛查看C++ 0x的維基百科條目所得到的結果。我在哪裏可以找到更多關於它的信息? – Warpspace 2010-04-06 06:03:20

+0

@Warpspace:下載N3092(谷歌搜索「C++ N3092」),查看Stroustrup的C++ 0x FAQ http://www2.research.att.com/~bs/C++0xFAQ.html。我注意到微軟對'emplace'有不正確的文檔,聲稱它使用移動語義。 (同樣的事情是通過'push_back(move(...))'完成的。)所以,如果你使用MSVC,那麼你現在可能還是不走運。 – Potatoswatter 2010-04-06 07:25:03

4

事實上,在這種情況下編譯器可能會刪除副本。

如果您的編譯器不這樣做,避免複製的一種方法是讓您的列表包含指針而不是實例。您可以使用智能指針爲您清理對象。

+2

編譯器無法刪除副本。該對象在堆棧上構建,並通過const引用傳遞給'vector <> :: allocator_type :: construct'將其移動到堆中。它是在對'push_back'的調用開始之前構建的,直到'push_back'內的'allocator_type :: allocate'才知道目標位置。時間和空間的力量不合作。 – Potatoswatter 2010-04-05 23:04:41

2

查看Boost的ptr_container庫。我用ptr_vector特別是:

boost::ptr_vector<Foo> c; 
c.push_back(new Foo(1,2,3)); 
c[0].doSomething() 

並且當其超出範圍,delete將在向量中的每個元素被調用。

+1

...沒有'shared_ptr'的開銷 - +1 – vladr 2010-04-06 02:50:16

1

使用shared_ptrshared_array來管理你的類想要分配的內存。然後,編譯器提供的拷貝構造函數將簡單地增加一個引用計數,因爲shared_ptr拷貝自己。對於標準容器來說,這是一個重要的使用概念,您的元素的複製便宜。標準庫在整個地方進行復制。

5

C++ 0x移動構造函數(可用於VC++ 2010和最近的GNU編譯器)正是你正在尋找的。

+0

移動構造函數需要對象的未初始化狀態。如果他有一個未初始化的狀態,他可以用'push_back(MyClass())'默認構建到那個狀態,然後'初始化'他的對象。所以我不認爲'移動'在這裏解決任何問題。 – Potatoswatter 2010-04-05 23:17:26

+0

我不確定它是否是我的編譯器,但啓用C++ 0x(在G ++下)後,移動構造函數似乎不被調用,因爲我的對象被刪除兩次(意味着使用了複製構造函數)。 Potatocorn提到的是我目前的實現,我試圖避免由於嚴重代碼重複(或需要專用功能)。 – Warpspace 2010-04-06 06:24:13

9

Ahem。科學的利益,我已經掀起了一個小的測試程序來檢查編譯器是否elides副本或不:

#include <iostream> 
#include <list> 
using namespace std; 

class Test 
{ 
public: 
    Test() { cout<<"Construct\n"; } 
    Test(const Test& other) { cout<<"Copy\n"; } 
    Test& operator=(const Test& other) { cout<<"Assign\n"; return (*this); } 
}; 

Test rvo() { return Test(); } 
int main() 
{ 
    cout<<"Testing rvo:\n"; 
    Test t = rvo(); 
    cout<<"Testing list insert:\n"; 
    list<Test> l; 
    l.push_back(Test()); 
} 

下面是我的MSVC++輸出2008:

 
Testing rvo: 
Construct 
Testing list insert: 
Construct 
Copy 

對於調試版本和發佈版本來說都是一樣的:RVO的工作原理,臨時對象傳遞並沒有被優化。
如果我沒有弄錯,在C++ 0x標準中添加Rvalue references是爲了解決這個問題。

+1

+1科學**! – 2010-04-05 22:44:54

0

我建議使用std::vector<std::unique_ptr>,因爲它會在需要時自動釋放內存,開銷比std::shared_ptr更少。唯一需要注意的是這個指針沒有引用計數,所以你必須小心,不要將指針本身複製到其他地方,以免數據在其他地方仍然被使用時被刪除。

相關問題