C++中的复制构造函数和赋值运算符

copy constructor and assignment operator in C++

class MyClass
{
private:
    int x;
public:
    MyClass(int x)
    {
        this->x = x;
    }
    MyClass(const MyClass& other)
    {
        this->x = other.x;
    }
};

int main()
{

    MyClass first(1);
    MyClass second = first;        //(1)
    first = second;               // (2)
    MyClass third = MyClass(2);  //  (3)     
}

为什么第一行 MyClass second = first 调用复制构造函数而第二行 first = second; 调用赋值运算符,为什么第三行 MyClass third = MyClass(2); 不像第一行那样调用复制构造函数。 最后,第 3 行到底做了什么 MyClass third = MyClass(2);.

构造函数,包括拷贝构造函数,用于初始化一个class对象。赋值运算符用于修改已经初始化的对象。

第 (1) 行调用复制构造函数,因为它正在初始化对象 second。赋值运算符与 = 符号也用于此语法这一事实无关。第 (2) 行赋值给已经存在的 first,因此使用赋值运算符。

在C++14及更早的第(3)行,允许编译器为MyClass(2)表达式创建一个临时对象,然后使用复制构造函数初始化third,然后摧毁暂时的。但是也允许编译器“elide”(去掉)临时对象、拷贝构造函数和临时析构函数,只是像原来的临时对象一样直接初始化third。这种复制省略是 C++ 允许优化的一种情况,这可能会导致可观察到的行为发生差异,因为复制构造函数 and/or 析构函数可能具有优化跳过的副作用(如打印痕迹)。

在 C++17 及之后的第 (3) 行中,我们说表达式 MyClass(2) 具有结果对象 third。所以表达式指定 third 是用参数 2 直接初始化的。不涉及复制构造函数或临时对象。 C++17 的此功能通常称为“强制复制省略”,因为其行为与编译器能够应用复制省略优化的 C++14 程序相同。但从技术上讲,这不是复制省略,因为不涉及要省略的副本。还有一些其他情况下,复制省略对于编译器来说是可选的。