8

考虑下面的代码的返回值初始化的对象时不叫:复制构造与功能

#include <iostream> 

using namespace std; 

class A 
{ 
    public: 
     int a; 
     A(): a(5) 
     { 
      cout << "Constructor\n"; 
     } 
     A(const A &b) 
     { 
      a = b.a; 
      cout << "Copy Constructor\n"; 
     } 
     A fun(A a) 
     { 
      return a; 
     } 
}; 

int main() 
{ 
    A a, c; 
    A b = a.fun(c); 
    return 0; 
} 

上面的代码的与g++ file.cpp的输出是:

Constructor 
Constructor 
Copy Constructor 
Copy Constructor 

的输出以上代码与g++ -fno-elide-constructors file.cpp是:

Constructor 
Constructor 
Copy Constructor 
Copy Constructor 
Copy Constructor 

我知道返回值优化通货膨胀。我的问题是哪个调用复制构造函数是被忽略的(返回期间的临时对象还是返回的对象被复制到b)?

如果消失的拷贝构造函数是用于创建b的拷贝构造函数,那么b是如何创建的(因为在这种情况下也没有构造函数调用)?

如果我将A b = a.fun(c);行替换为a.fun(c)并使用第一种方法甚至第二种方法进行编译,那么复制构造函数也会被调用2次。所以,如果在上一段中解释过的情况下,临时对象的拷贝构造函数被省略了,那么为什么在这种情况下它不被忽略?

+0

我怎么在我了解到这一切时检查了这些东西,所有东西都是'std :: cout <<“复制构造函数:”<<(void *)b <<“to”<<(void *)this << std :: endl;' ,和'std :: cout <<“构造”<<(void *)this << std :: endl'。如果你在C++中添加移动,奖励点数11。 – IdeaHat 2014-11-14 19:20:39

回答

6
#include <iostream> 

using namespace std; 

class A 
{ 
public: 
    int a; 
    A(): a(5) 
    { 
     cout << "Constructing: " << (void *)this << std::endl; 
    } 
    A(const A &b) 
    { 
     a = b.a; 
     cout << "Copy Constructor: " << (void *)this << " from " << (void *)&b << std::endl; 
    } 
    A fun(A a) 
    { 
     return a; 
    } 
}; 

int main() 
{ 

    A a, c; 
    A b = a.fun(c); 

    std::cout << "a:" << (void *)&a << std::endl << 
       "b:" << (void *)&b << std::endl << 
       "c:" << (void *)&c << std::endl; 
    return 0; 
} 

收率:

Constructing: 0x7fffbb377220 
Constructing: 0x7fffbb377210 
Copy Constructor: 0x7fffbb377230 from 0x7fffbb377210 
Copy Constructor: 0x7fffbb377200 from 0x7fffbb377230 
a:0x7fffbb377220 
b:0x7fffbb377200 
c:0x7fffbb377210 

所以它构造a,构建c,拷贝c到中间(该函数的自变量a),然后将中间件直接复制到b,跳过典型的a复制到返回中间。如果按值传递(改变A fun(const A& a)这是更好的证明:

Constructing: 0x7fff8e9642b0 
Constructing: 0x7fff8e9642a0 
Copy Constructor: 0x7fff8e964290 from 0x7fff8e9642a0 
a:0x7fff8e9642b0 
b:0x7fff8e964290 
c:0x7fff8e9642a0 

一个构造,C构造,C是直接复制到b,尽管b没有传递到乐趣

4

被遗漏的副本是将临时返回值复制到b中。如果没有省略号,返回值将从a初始化并复制到b。相反,暂时保留返回值的临时数据是,它构造成b并用a初始化。 [class.copy]/31:

当尚未结合至参考 (12.2)临时类对象将被复制/移动到一个类对象具有相同 CV-不合格类型,复制/移动操作可以通过 直接构建临时对象到 省去拷贝的目标可以省略/移动

您可以观察到这一点,如果你在fun添加额外的输出:

A fun(A a) 
{ 
    cout << "fun!" << endl; 
    return a; 
} 

然后与省音,你会得到

[...]
乐趣!
拷贝构造函数

而且没有:

[...]
乐趣!
复制构造
拷贝构造