先看代码:
以下代码编译时要加:-fno-elide-constructors 来关闭编译器优化,否则可能看不到复制构造函数的调用。
#include <iostream>
using std::cout;
using std::endl;
class A {
public:
A() = default;
A(A& a)
{
cout << "copy constructor1 is called." << endl;
}
A(const A& a)
{
cout << "copy constructor2 is called." << endl;
}
};
A createA()
{
return A();
}
A createA2()
{
A a;
return a;
}
int main()
{
A a;
A b = a;
cout << "==============" << endl;
createA();
cout << "==============" << endl;
cout << "==============" << endl;
createA2();
cout << "==============" << endl;
A d = createA2();
return 0;
}
输出结果:
copy constructor1 is called.
===============
copy constructor2 is called.
===============
copy constructor2 is called.
copy constructor2 is called.
===============
copy constructor1 is called.
===============
copy constructor1 is called.
copy constructor2 is called.
下面主要解释一下A c = createA(); 为什么执行第二个复制构造函数。
我们知道,C++运行时会产生临时对象,而这个临时对象我们是不能对其进行操作的,比如修改这个临时变量的值是做不到的,因为这个临时变量由编译器复杂生成,对我们不可见。
而C++认为如果一个函数的参数是一个普通引用(非const),那么这个变量(实参)应该是能够被修改的,比如我们传入一个普通变量是没有问题的,这个普通变量是可以修改的。
但是临时变量对我们是不可修改的,那么临时变量就不能作为普通引用(非const)传入函数(违反了上面所说的编译器的要求:可以被修改)
所以当A c = createA(); 时,createA()的返回值是一个临时变量,那么它不能作为普通引用(非const), 也就是不能调用第一个复制构造函数,必须调用第二个复制构造函数。(如果注释掉第二个,编译不通过)
那么在createA()和createA2()时为什么调用不同的复制构造函数呢?
函数返回时调用复制构造函数其实是为那个临时变量进行构造(生成临时变量),而参数就是return 后面的对象(return A(), return a;),
对于return A()来说 A()是也是一个临时变量,分析同上,也要调用第二个复制构造函数。
对于return a来说,这个a,它不是一个临时变量(我们自己定义的),所以可调用第一个复制构造函数。
A c = createA()或A d = createA2()过程大体如下:
S1就是return后面的对象(A(), a), 对于A()是个临时的,a不是临时对象(调用第一次copy constructor时的参数,决定了调用哪个copy constructor)
S2就是函数的返回值空间(对象),一定是临时的。(调用第二次copy constructor时的参数,决定了调用第二个copy constructor)
S3是变量c/d。
编译器对返回值的一般处理方式:
主调函数创建一段内存空间用于存放返回的对象(也就是上图中的临时空间S2),主调函数调用被调函数时,将该内存空间作为参数传入被调函数中(编译器自动完成,无需程序员自己显性写出)。因此,返回值所生成的对象的内存分配和构造是分步执行的,在主调函数中分配内存,在被调函数中进行构造。
以上为个人理解,如有错误,还望指正。
参考:
https://blog.youkuaiyun.com/qq_41791653/article/details/82354114
https://www.cnblogs.com/area-h-p/p/11498481.html