C++11多次移动构造函数调用

3
我已经使用 g++ -std=c++11 file.cpp 来编译了这个程序,现在我正在学习 C++ 中的移动构造函数,但感到有些困惑。
#include <iostream>
#include <string>
#include <vector>
using namespace std;

class P {
public:
    string* ptr_;
    P(string name) { ptr_ = new string(name); }
    ~P() { delete ptr_; }
    P(P&& pother) : ptr_(move(pother.ptr_)) { 
        cout<<"move"<<endl; 
        pother.ptr_=nullptr; 
    }
    void print() {cout << *ptr_ << endl;}
};

int main()
{
    vector<P> ppl;
    ppl.push_back(P("Jojo"));
    ppl.push_back(P("Jojo"));
    ppl.push_back(P("Jojo"));
}

该程序的输出结果是:
$ ./a.out 
move
move
move
move
move
move

在这里为什么会调用6次移动构造函数?

1
在每次调用 push_back 之前和之后打印出向量的容量和大小,这将有助于理解该主题。 - Benjamin Lindley
1
ptr_(move(pother.ptr_)) 实际上不会“移动”任何东西,因为它是一个指针。可以是 ptr_(pother.ptr_)。这并没有错,但指针、整数或任何基本类型都不是使通用引用有趣的东西。 - Eljay
1个回答

5
因为std::vector::push_back会导致重新分配内存,当新的size()大于capacity()时,vector会分配新的底层存储,并通过移动构造函数将所有当前元素移动到新存储中。在重新分配期间移动元素会导致多次调用移动构造函数。
如何增加容量并未明确规定,我猜测每次重新分配时都会加倍容量。
ppl.push_back(P("Jojo")); // 0 element(s) moved, 1 element added, 1 move(s) in all; size=1, capacity=1
ppl.push_back(P("Jojo")); // 1 element(s) moved, 1 element added, 2 move(s) in all; size=2, capacity=2
ppl.push_back(P("Jojo")); // 2 element(s) moved, 1 element added, 3 move(s) in all; size=3, capacity=4

// assume the 4th push_back is performed
ppl.push_back(P("Jojo")); // 0 element(s) moved, 1 element added, 1 move(s) in all; size=4, capacity=4

因此,移动构造函数被调用了6次。顺便说一下,如果您执行push_back操作多一次,只会调用一次移动构造函数;因为对于第4个push_back,不会发生重分配。

您可以使用std::vector::reserve来避免重新分配。

vector<P> ppl;
ppl.reserve(3); // prohibit reallocations for the following 3 push_back
ppl.push_back(P("Jojo"));
ppl.push_back(P("Jojo"));
ppl.push_back(P("Jojo"));

为什么这个代码是良好的形式,即使它的移动构造函数不是“noexcept”? - Johannes Schaub - litb
@JohannesSchaub-litb 因为没有复制构造函数?我未能在标准中确认移动构造函数是否必须是noexcept,或者必须有可用的复制构造函数进行重新分配。 - songyuanyao

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接