一:优化的规则
在一行代码中连续进行:构造+构造/构造+拷贝构造/拷贝构造+拷贝构造 都会合二为一
如下:
a:构造+构造->构造
b:构造+拷贝构造->构造
c:拷贝构造+拷贝构造->拷贝构造
注意:不同的编译器优化的效果可能会不同(博主是在 VS19 下的 Debug 版本下进行演示)
二: 优化展示
以下是一个用于测试的类A,其有构造函数,拷贝函数,赋值重载函数,构析函数,进入每个函数都会打印调用了一次 xx函数 的字眼,这样更能显式的看出优化
class A
{
public:
//构造函数
A(int a = 0)
:_a(a)
{
cout << "A(int a)调用一次构造" << endl;
}
//拷贝函数
A(const A& aa)
:_a(aa._a)
{
cout << "A(const A& aa)调用一次拷贝" << endl;
}
//赋值重载函数
A& operator=(const A& aa)
{
cout << "A& operator=(const A& aa)调用一次赋值" << endl;
if (this != &aa)
{
_a = aa._a;
}
return *this;
}
//构析函数
~A()
{
cout << "~A()调用一次构析" << endl;
}
private:
int _a;
};
①:在一行代码的构造+拷贝
情况1:
int main()
{
A aa1 = 1;
return 0;
}
解释:
a:aa1是A类实例化的对象,而1是整形,这叫作类型转换
b:类型转换 的规则如下:
所有的类型转换都是不会影响到本身的,图中来说就是不会影响到1,并不是1真的变成了一个对象,而是1先作为参数进行了匿名构造,构造出一个中间的对象(且中间变量都是具有常属性,即不能被更改),然后这个中间对象再去进行拷贝构造出了aa1(用一个已存在的对象去初始化一个未存在的对象,调用拷贝构造)
c:所以这里在同一行代码中进行构造+拷贝构造,所以优化成了:
结果:
解释:正如前文的规则,优化成了只调用了一次构造(构析是aa1出了main函数调用了)
Q:为什么不是直接进行拷贝构造呢?为什么要生成中间变量?不会压根没有中间变量的生成吧?
A:如果没有中间变量的生成,那从1到aa1就应该是变量到变量,那为何以下这样写不行呢?
A& aa1 = 1;
这样不是只是对生成的aa1进行了引用而已吗?为什么就不行了呢。报错如下:
正确写法:
const A& aa1 = 1;
解释:const修饰的是常变量,具有不可变的性质,只能这样写也说明了,的确是有中间变量的产生,其进行拷贝构造给了aa1,因为中间变量是有常属性的,我们的aa1被常属性的对象初始化,所以我们的aa1的引用才需要const修饰
情况2:
void func(A aa1)
{
}
int main()
{
//匿名构造仅是构造(不是类型转换,谈何构造加拷贝),然后再拷贝传参
func(A(2));
return 0;
}
解释:
a:func(A(2)),中的A(2)是一次匿名构造,调用了一次构造函数
b: A(2)构造出来的对象传入func函数,调用了一次拷贝函数
c:所以是一次构造 + 拷贝,但是是在同一行上连续进行的,所以优化成了:
情况3:
void func(A aa1)
{
}
//构造+拷贝构造->构造
int main()
{
//3 到 aa1 是隐示类型转换 先构造 在拷贝
func(3);
return 0;
}
解释:
a:3直接作为参数给func函数的aa1接收,这是类型转换,如情况1中的意义,先进行构造,再进行拷贝,所以 应该是 在一行上进行 构造 + 拷贝,优化成了:
②:不在一行的的构造 + 拷贝
void func(A aa)
{
}
int main()
{
A aa1(1);
func(aa1);
return 0;
}
解释:
a:A aa1(1),是一次构造函数的调用
b:func(aa1),将aa1传给了func函数,是一次拷贝函数的调用
c:所以这是 构造 + 拷贝函数 ,但是不会被优化,因为其不是在一行上连续进行的
结果:
两次构析是aa 和 aa1 离开自己的作用域调用的
③:在一行的的拷贝+ 拷贝
A func()
{
A aa;
return aa;
}
int main()
{
A aa1 = func();
return 0;
}
解释:
a:func函数中 aa是一次构造,return aa 是一次拷贝 ,A aa1 中的 aa1 还未存在,此时去接受 func函数return 出来的对象,这是一次拷贝,演示后两次拷贝的优化效果
结果:
解释:后两次拷贝优化成了一次
④:不在一行的的构造+构造
A func()
{
A aa;
return aa;
}
int main()
{
A aa2;
aa2 = func();
return 0;
}
解释:
a:A aa2是调用一次构造
b:func 则进入函数,A aa 是一次构造
c:return aa 是一次拷贝
d: 函数返回的对象给 aa2 是一次赋值(两个存咋的对象用 = 叫作赋值)
这个对前两次的构造进行展示,因为不是一行上的连续构造,所以不进行优化
结果:
总结:
一行上连续的构造 + 构造 = 一次构造
一行上连续的构造 + 拷贝 = 一次构造
一行上连续的拷贝 + 拷贝 = 一次拷贝