为什么移动构造函数与复制构造函数一起调用?

4

这个问题看起来很奇怪,但我已经在多个编译器中检查过了。在我的代码中,我有一个移动构造函数和一个拷贝构造函数

class A {
    int val;
public:
    A(int var) : val(var)  {
    }
    A( A && a1) {
        cout<<"M Value -> "<<a1.val<<endl;
        cout<<"Move Cons..."<<endl;
     }
    A(const A & a1) {
        cout<<"Copy Cons.."<<endl;
        cout<<"Value -> "<<a1.val<<endl;
    }
};

如果我将我的main函数写成这样:
int main()
{
    vector<A> v1;
    A a2(200);
    v1.push_back(move(a2));              
}

输出结果为:
M Value -> 200
Move Cons...

预计会出现这种情况,但如果我将我的main函数更改为

int main()
{
    vector<A> v1;
    A a2(200);
    v1.push_back(A(100));
    v1.push_back(move(a2));

}

我得到了以下输出。
M Value -> 100
Move Cons...
M Value -> 200
Move Cons...
Copy Cons..   // unexpected
Value -> 0    // unexpected

谁能帮助我理解这个复制构造函数在哪里以及如何被调用,特别是当值为0时。

谢谢


7
为了扩展向量,向量需要重新分配其存储空间,并将第一个元素复制到新的存储空间中。将移动构造函数标记为“noexcept”,然后良好的实现可以移动元素而不是复制元素。 - Igor Tandetnik
5
关于值为0 - 你的复制和移动构造函数实际上并没有复制任何东西,也没有初始化this->val。当进一步移动或复制这样的对象时,您的程序会出现未定义行为,因为访问未初始化的对象。在您的情况下,val中的不可预测值恰好为0。 - Igor Tandetnik
2
大多数(至少是符合规范的)std::vector标准库实现只有在它们包含的元素满足std::move_if_noexcept时才会在增长时移动元素,因为vector需要实现强异常保证。如果它们无法移动,则会退回到复制。请将您的移动构造函数设置为noexcept - Jesper Juhl
你也可以在推回前预留存储空间,以避免向量中的重新分配:v1.reserve(2); - ks1322
Daksh,我回答了你的问题。 - Jive Dadson
1个回答

1
这些原因已经在评论中回答了。我会尝试说明正在发生的事情。
步骤: vector<A> v1; 分配向量。它在内部为一个元素保留空间。

v1.push_back(A(100));

  1. 构造并移动向量中的 A(100)

v1.push_back(move(a2));

  1. 由于您正在尝试插入一个新元素,超过了实际最大大小,因此向量被重新分配到新的内存空间。请记住,std::vector将其内容保存在连续的内存中。
  2. a2移动到v1内部。
  3. 将原始v1的其余元素(在本例中仅为第一个元素)复制到新的v1中。

内存:

1) ## v1[undef ] #########################
2) ## v1[A(100)] #########################
3) ##   [A(100)] ### v1[undef,  undef ] ##
4) ##   [A(100)] ### v1[undef,  A(200)] ##
5) ##   [A(100)] ### v1[A(100), A(200)] ##

附加说明:

关于值0,这是因为这些复制和移动构造函数实际上什么也没有做,val的值保持未定义。 使用适当的构造函数,此日志应该为100。

如果将移动构造函数标记为noexcept,它将在重新分配过程中代替复制构造函数。

您可以使用v1.emplace_back(100)而不是v1.push_back(A(100))来避免移动。


1
空向量可能没有为一个元素分配空间,但第一次调整大小是不可见的。 - Caleth
@Caleth,你很可能是对的。由于向量增长是实现相关的,并且我们不使用显式调整大小,因此不清楚。因此,为了清晰起见,我试图尽可能保持简单。 - Germán

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