2013-03-06 107 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); 
相关问题