何时调用Move Constructor?

Lau*_*uer 23 c++ move-constructor c++11

我很困惑何时调用移动构造函数与复制构造函数.我已经阅读了以下资料:

移动构造函数未在C++ 0x中调用

在C++ 11中移动语义和右值引用

MSDN

所有这些来源要么过于复杂(我只想要一个简单的例子),要么只展示如何编写移动构造函数,而不是如何调用它.我写了一个简单的问题更具体:

const class noConstruct{}NoConstruct;
class a
{
private:
    int *Array;
public:
    a();
    a(noConstruct);
    a(const a&);
    a& operator=(const a&);
    a(a&&);
    a& operator=(a&&);
    ~a();
};

a::a()
{
    Array=new int[5]{1,2,3,4,5};
}
a::a(noConstruct Parameter)
{
    Array=nullptr;
}
a::a(const a& Old): Array(Old.Array)
{

}
a& a::operator=(const a&Old)
{
    delete[] Array;
    Array=new int[5];
    for (int i=0;i!=5;i++)
    {
        Array[i]=Old.Array[i];
    }
    return *this;
}
a::a(a&&Old)
{
    Array=Old.Array;
    Old.Array=nullptr;
}
a& a::operator=(a&&Old)
{
    Array=Old.Array;
    Old.Array=nullptr;
    return *this;
}
a::~a()
{
    delete[] Array;
}

int main()
{
    a A(NoConstruct),B(NoConstruct),C;
    A=C;
    B=C;
}
Run Code Online (Sandbox Code Playgroud)

目前A,B和C都有不同的指针值.我希望A有一个新指针,B有C的旧指针,C有空指针.

有点偏离主题,但如果有人可以建议我可以详细了解这些新功能的文档,我将不胜感激,可能不需要再提出更多问题.

asc*_*ler 33

调用移动构造函数:

  • 当一个对象初始化器是 std::move(something)
  • 当一个对象初始化器是std::forward<T>(something)T不是一个左值引用类型时(在"完美转发"的模板编程中很有用)
  • 当对象初始值设定项是临时的并且编译器不完全消除复制/移动时
  • 按值返回函数本地类对象时,编译器不会完全消除复制/移动
  • 抛出函数本地类对象时,编译器不会完全消除复制/移动

这不是一个完整的列表.请注意,如果参数具有类类型(不是引用),则"对象初始值设定项"可以是函数参数.

a RetByValue() {
    a obj;
    return obj; // Might call move ctor, or no ctor.
}

void TakeByValue(a);

int main() {
    a a1;
    a a2 = a1; // copy ctor
    a a3 = std::move(a1); // move ctor

    TakeByValue(std::move(a2)); // Might call move ctor, or no ctor.

    a a4 = RetByValue(); // Might call move ctor, or no ctor.

    a1 = RetByValue(); // Calls move assignment, a::operator=(a&&)
}
Run Code Online (Sandbox Code Playgroud)

  • @artm是的,这取决于编译器。实际上,C ++ 17稍微修改了规则,以便在某些其他情况下保证更少的构造函数和析构函数调用。 (2认同)

Pra*_*ian 5

首先,你的复制构造函数坏了。复制自和复制到对象都将指向相同的对象Arraydelete[]并且在超出范围时都将尝试使用它,从而导致未定义的行为。要修复它,请复制数组。

a::a(const a& Old): Array(new int[5])
{
  for( size_t i = 0; i < 5; ++i ) {
    Array[i] = Old.Array[i];
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,移动赋值并没有像您希望的那样执行,因为两个赋值语句都是从左值进行分配,而不是使用右值。对于要执行的移动,您必须从一个右值移动,或者它必须是一个可以将左值视为右值的上下文(例如函数的 return 语句)。

要获得所需的效果,请使用std::move创建右值引用。

A=C;              // A will now contain a copy of C
B=std::move(C);   // Calls the move assignment operator
Run Code Online (Sandbox Code Playgroud)