【问题标题】:Why does the map.insert() method invoke the copy constructor twice?为什么 map.insert() 方法会调用复制构造函数两次?
【发布时间】:2013-03-06 12:23:09
【问题描述】:

我正在创建自定义类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()

【问题讨论】:

    标签: c++ map copy-constructor


    【解决方案1】:

    很可能是因为您的映射的值类型是pair&lt;int const, Node&gt;,而不是pair&lt;int, Node&gt;:在映射中,键是常量

    由于insert() 接受pair&lt;int const, Node&gt; const&amp; 并且您提供pair&lt;int, Node&gt;,因此必须构造一个临时对象,然后可以从中复制映射中的值。

    要验证它,请更改此行:

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

    进入这一行:

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

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

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

    【讨论】:

      【解决方案2】:

      您正在使用pair&lt;int,Node&gt;。 insert 方法采用的类型是map&lt;K,V&gt;::value_type,定义为pair&lt;const K,V&gt;。 编译器必须插入额外的副本才能在这两种类型之间进行转换。

      尝试使用map&lt;int,Node&gt;::value_type 而不是pair&lt;int,Node&gt;。最好使用类本身定义的类型,而不是从头开始重新创建它们。

      你也可以通过写作来避免你的第一份副本。

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

      而不是

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

      【讨论】:

        【解决方案3】:

        当您执行以下操作时:

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

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

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

        mapping.insert(toInsert);
        

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

        【讨论】:

        • 您的解释中的副本#3 在哪里? OP 的帖子显示了一份 toInsert 副本(您已解释),以及两份 insert 副本(您只解释了一份)。另外,我认为您对第二部分的解释不正确。容器复制并插入它们的原因是设计使然(即它们承诺您这样做,以便插入范围内的对象保持不变),而不是因为生命周期的原因。如果你不想这样,你可以使用emplacestd::move
        • 有趣的是,我似乎误读了这个问题。好吧,我现在也从接受的答案中学到了一些东西。哦,好吧。
        猜你喜欢
        • 1970-01-01
        • 2020-06-29
        • 1970-01-01
        • 2015-04-28
        • 1970-01-01
        • 2021-05-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多