8

以下是“C++ Gotchas”一书第 56 项的摘录:

以三种不同方式编写 Y 对象的简单初始化并不少见,就好像它们是等价的一样。

Y a( 1066 ); 
Y b = Y(1066);
Y c = 1066;

事实上,所有这三个初始化都可能导致生成相同的目标代码,但它们并不等价。a 的初始化被称为直接初始化,它完全符合人们的预期。初始化是通过直接调用 Y::Y(int) 来完成的。

b 和 c 的初始化更复杂。事实上,它们太复杂了。这些都是复制初始化。在初始化 b 的情况下,我们请求创建一个类型为 Y 的匿名临时变量,初始化值为 1066。然后我们使用这个匿名临时变量作为 Y 类的复制构造函数的参数来初始化 b。最后,我们调用匿名临时的析构函数。

为了测试这一点,我做了一个带有数据成员的简单类(最后附加了程序),结果令人惊讶。似乎对于 c 的情况,对象是由复制构造函数构造的,而不是像书中建议的那样。

有谁知道语言标准是否发生了变化,或者这仅仅是编译器的优化功能?我使用的是 Visual Studio 2008。

代码示例:

#include <iostream>

class Widget
{
    std::string name;
public:
    // Constructor
    Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; }
    // Copy constructor
    Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; }
    // Assignment operator
    Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; }
};

int main(void)
{
    // construct
    Widget a("a");
    // copy construct
    Widget b(a);
    // construct and assign
    Widget c("c"); 
    c = a;
    // copy construct!
    Widget d = a;
    // construct!
    Widget e = "e";
    // construct and assign
    Widget f = Widget("f");

    return 0;
}

输出:

Constructing Widget a

Copy constructing Widget from a

Constructing Widget c
Assigning Widget from a to c

Copy constructing Widget from a

Constructing Widget e

Constructing Widget f
Copy constructing Widget from f

我对构建 d 和 e 的结果感到最惊讶。准确地说,我期望创建一个空对象,然后创建一个对象并将其分配给空对象。实际上,对象是由复制构造函数创建的。

4

3 回答 3

16

语法

X a = b;

其中 a 和 b 属于 X 类型始终意味着复制构造。任何变体,例如:

X a = X();

被使用,没有分配正在进行,从来没有。构造和分配将类似于:

X a;
a = X();
于 2010-03-17T14:05:28.093 回答
6

允许编译器优化案例bca. 此外,编译器无论如何都可以完全消除复制构造和赋值运算符调用,因此您看到的任何内容对于不同的编译器甚至编译器设置都不一定相同。

于 2010-03-17T14:03:33.723 回答
3

从 C++17 开始,所有这三个都是等价的(除非Y::Y(int)is explicit,这将简单地禁止c),因为通常称为强制复制省略

EvenY c = 1066;只创建一个Y对象,因为隐式转换的结果Y是一个纯右值,用于初始化c而不是创建临时值。

于 2017-09-15T03:28:30.183 回答