5

我正在创建自定义类以使用容器Node实现二叉树:映射的键是对象的标识符。在类中,我必须实现一个复制构造函数。map<int,Node>intNodeNode

在地图上插入一个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()
4

3 回答 3

14

很可能是因为您的地图的值类型是pair<int const, Node>,而不是pair<int, Node>:在地图中,键是常量

由于insert()接受 apair<int const, Node> const&并且您提供 a pair<int, Node>,因此要执行转换,必须构造一个临时对象,然后可以从该临时对象中复制构造映射中的值。

要验证它,请更改此行:

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

进入这一行:

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

您应该会看到对复制构造函数的额外调用消失了。

还要记住,标准库容器的具体实现不需要执行特定数量的副本:实现可能会有所不同,不同的优化级别也会使事情变得不同。

于 2013-03-06T12:25:18.030 回答
1

您正在使用pair<int,Node>. insert 方法采用的类型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);
于 2014-04-30T07:29:47.980 回答
-1

当您执行以下操作时:

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

您正在将 传递给对象node2的构造pair函数。即使您通过引用传递,从概念上讲,您将绑定在一起,这意味着pair对象正在制作对象的副本node2。复制 #1。

当您将此pair对象传递给插入函数时:

mapping.insert(toInsert);

.. 是的,您是通过引用传递的,但容器对所引用对象的生命周期一无所知(toInsert)。因此,它将自己的副本存储在容器中。复制 #2。

于 2013-03-06T12:35:25.367 回答