c++何时向量push_back深复制对象?

1 c++ destructor copy-constructor move-constructor

我创建了一个向量并使用push_back将几个节点对象放入其中。但是,我无法预测何时将使用移动构造函数或复制构造函数。

当push_back使用复制构造函数或移动构造函数时有什么模式吗?c++参考说它有时会复制,有时会移动,但从未详细说明它们何时做什么。

#include <iostream>
#include <vector>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include <map>
#include <queue>
using namespace std;
struct Node {
    int val;
    
    Node(int val) : val(val) {
        cout<<"created object" << val<<endl;
    }
    
    Node(const Node& m) : val(m.val) {
        cout<<"copy constructor is called on value " << m.val << endl;
    }
    
    ~Node() {
        cout<<"destroyed val" << val<<endl;
    }


    Node(Node&& other) noexcept 
        : val(other.val) {
            cout<<"moved val " << other.val << endl;
    }


};


void f(vector<Node>& a) {
    cout<<"______________________"<<endl;
    Node tmp(12);
    cout<<"12 established"<<endl;
    a.push_back(tmp);
    cout<<"a pushed back 12"<<endl;
    a.push_back(Node(14));
    
    cout<<"a pushed back tmp obj 14"<<endl;
    
    tmp.val+=5;
    
    cout<<"increased tmp.val"<<endl;
    cout<<tmp.val<<endl;
    cout<<a[1].val<<endl;
    cout<<"two prints"<<endl;
    cout<<"_______________"<<endl;
    cout<<"end of f"<<endl;
    // return a;
}

int main() {
    
    vector<Node> a = {Node(125)}; //copied since initialized temp var.
    a.reserve(4000);
    cout<<"start of f"<<endl;
    f(a);

    
    cout<<"program ended"<<endl;


    //noteiced: Copy constructor called upon local variable (12) that the vector knows will not stay with it --- and belongs to local scope.
    //copy constructor not called upon temporary variable that soon belonged to vector (14).
    //same thing with std::queue, std::stack, and many others.
//but it this a pattern or a coincidence?
}



Run Code Online (Sandbox Code Playgroud)

输出:

copy constructor is called on value 125
destroyed val125
moved val 125
destroyed val125
start of f
______________________
created object12
12 established
copy constructor is called on value 12
a pushed back 12
created object14
moved val 14
destroyed val14
a pushed back tmp obj 14
increased tmp.val
17
12
two prints
_______________
end of f
destroyed val17
program ended
destroyed val125
destroyed val12
destroyed val14```
Run Code Online (Sandbox Code Playgroud)

Sam*_*hik 5

规则非常简单。

std::vector::push_back有两个重载。

第一个重载采用一个const T &参数。调用该重载使用对象的复制构造函数。

第二个重载采用一个T &&参数。调用该重载使用对象的移动构造函数。

请注意,两个重载最终可能会重新分配向量,这将触发一大堆移动,这不是重点。我还没有列出所有代码的诊断输出,但也有可能您正在记录由于重新分配而导致的移动,这进一步使问题变得混乱(尽管我看到您曾经reserve()摆脱过这一点,但它reserve()是在非-空向量,因此它唯一存在的值被移动,你可能也会对此感到困惑)。

所有这一切都假设T实现了移动语义。如果没有,那就都是复印件。

因此,您要做的只是确定给定的特定调用是否push_back()传递左值或右值。

    a.push_back(tmp);
Run Code Online (Sandbox Code Playgroud)

tmp显然是一个左值。这最终将调用复制构造函数。

    a.push_back(Node(14));
Run Code Online (Sandbox Code Playgroud)

该参数是一个右值。这最终将调用移动构造函数。

您可以使用相同的原理计算出其余的调用。

  • @qwert - push_back 是 O(1),因为它复制一个对象。它并没有说所有对象的复制成本都相同。 (2认同)