C ++ copy-construct构造和分配问题

时间:2010-03-17 14:00:25

标签: c++ constructor copy-constructor assignment-operator

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

  

看到一个简单的事情并不罕见   初始化写入的Y对象   三种不同方式中的任何一种,好像   它们是等价的。

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

事实上,所有这三个   可能会导致初始化   在相同的目标代码中   生成,但它们不等同。   a的初始化称为a   直接初始化,它确实如此   正是人们所期待的。该   初始化是通过完成   直接调用Y :: Y(int)。

     

b和c的初始化是   更复杂。事实上,他们也是   复杂。这些都是副本   初始化。在的情况下   初始化b,我们正在请求   创建一个匿名临时   类型Y,用值初始化   然后我们使用这个匿名临时作为副本的参数   要初始化的类Y的构造函数   湾最后,我们将析构函数称为   匿名临时。

为了测试这个,我做了一个带有数据成员的简单类(最后附加了程序),结果令人惊讶。似乎对于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的结果感到非常惊讶。确切地说,我期望创建一个空对象,然后创建一个对象并将其分配给空对象。实际上,对象是由复制构造函数创建的。

3 个答案:

答案 0 :(得分:16)

语法

X a = b;

其中a和b属于X类,总是意味着复制构造。无论变种,例如:

X a = X();

被使用,没有任何分配,从来没有。构造和赋值将类似于:

X a;
a = X();

答案 1 :(得分:6)

允许编译器优化案例bca相同。此外,编译器无论如何都可以完全消除复制构造和赋值操作符调用,因此无论您看到的是什么,不一定与不同的编译器甚至编译器设置相同。

答案 2 :(得分:1)

从C ++ 17开始,这些中的所有三个都等效(除非Y::Y(int)explicit,因为什么只是c}通常称为mandatory copy elision

即使Y c = 1066;只创建一个Y对象,因为隐式转换为Y的结果是用于初始化c的prvalue,而不是创建暂时的。