为什么map.insert()方法调用复制构造函数两次?

Why does the map.insert() method invoke the copy constructor twice?

本文关键字:构造函数 两次 调用 map insert 方法 为什么 复制      更新时间:2023-10-16

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

当在映射中插入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()

很可能是因为映射的值类型是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);

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

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

您正在使用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);

当您执行以下操作时:

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

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

当你将pair对象传递给insert函数时:

mapping.insert(toInsert);

. .是的,你是通过引用传递,但是容器不知道任何关于引用对象的生命周期(toInsert)。所以它创建了自己的副本来存储在容器中。# 2副本。