2013-03-06 150 views
3

我正在創建自定義類Node以便使用map<int,Node>容器實現二叉樹:映射的int鍵是Node對象的標識符。在類Node我不得不實現一個複製構造函數。爲什麼map.insert()方法調用拷貝構造函數兩次?

當在地圖上插入一個Node對象時,我注意到Node的複製構造函數被調用兩次。爲什麼?

cout << "node2" << endl; 
Node node2; 
node2.set_depth(2); 
node2.make_it_branch(3,4); 

cout << "map" << endl; 
map<int,Node> mapping; 
cout << "toInsert" << endl; 
pair<int,Node> toInsert = pair<int,Node>(2,node2); 
cout << "insert" << endl; 
mapping.insert(toInsert); 

運行上面的代碼,輸出如下:

node2 
--- Node() 
map 
toInsert 
--- Node(const Node& orig) 
insert 
--- Node(const Node& orig) // Why does the copy constructor be invoked twice? 
--- Node(const Node& orig) // ------------------------------------------------ 
--- ~Node() 
--- ~Node() 
--- ~Node() 
--- ~Node() 

回答

12

很有可能是因爲你的地圖的值類型爲pair<int const, Node>,不pair<int, Node>:在地圖中,關鍵是不變

由於insert()接受pair<int const, Node> const&並且您提供了pair<int, Node>,要執行轉換,必須構建一個臨時映射,該映射中的值可以進行復制構建。

爲了驗證它,改變這一行:

pair<int, Node> toInsert = pair<int, Node>(2, node2); 

進入這一行:

pair<int const, Node> toInsert = pair<int const, Node>(2, node2); 

,你應該看到的拷貝構造函數的額外調用消失。

也請記住,標準庫容器的具體實現並不需要進行副本的具體數量:實現可能會有所不同,而不同的優化水平可能讓事情變得不同的。

-1

當你做到以下幾點:

toInsert = pair<int, Node>(2, node2); 

您傳遞node2pair對象的構造。即使你是通過引用傳遞,在概念上你的結合在一起,這意味着pair對象使node2對象的副本。複製#1。

當你通過這個pair對象插入功能:

mapping.insert(toInsert); 

..是的,你是通過引用傳遞,但容器不知道引用(toInsert)對象的生命週期什麼。所以它使自己的副本存儲在容器中。複製#2。

+1

哪裏是副本#3你的解釋? OP的帖子顯示了toInsert(你解釋過)的一個副本,以及兩個插入副本(你只能解釋一個副本)。另外,我不認爲你對第二部分的解釋是正確的。容器製作副本並插入它們的原因是設計上的(即它們保證你這樣做,以便你的對象在插入範圍內保持原狀),而不是因爲生命的原因。如果你不想要,你可以使用'emplace'或'std :: move'。 – us2012 2013-03-06 12:37:58

+0

好笑,看來我誤解了這個問題。那麼,我現在也從已接受的答案中學到了一些東西。好吧。 – Bingo 2013-03-06 12:40:08

1

您正在使用pair<int,Node>。 通過插入方法所採取的類型是map<K,V>::value_type其被定義爲pair<const K,V>。 編譯器必須插入一個額外的副本才能在這兩種類型之間進行轉換。

嘗試使用map<int,Node>::value_type代替pair<int,Node>。最好使用類自身定義的類型,而不是從頭開始重新創建它們。

你也可以通過書寫來避免你的第一個副本。

map<int,Node>::value_type toInsert(2,node2); 

代替

map<int,Node>::value_type toInsert = map<int,Node>::value_type(2,node2); 
相關問題