1.先思考下面代码一共调用了多少次拷贝构造函数???
class Widget
{
public:
Widget()
{
//cout << "Widget()" << endl;
}
Widget(const Widget&)
{
cout << "Widget(const Widget&)" << endl;
}
};
Widget f(Widget u)
{
return u;
}
int main()
{
Widget x;
Widget y = f(x);
return 0;
}
我们正常的逻辑是----如下图
调用3次拷贝构造函数
可事实上 --- 我们程序却只调用了两次拷贝构造函数
事实上编译器会在传参和传返回值的过程中将连续拷贝构造函数合二为一
省去中间变量的过程 --直接将u拷贝构造给y
那么当我们换一种情况时,编译器的拷贝构造优化就失效了-----
class Widget
{
public:
Widget()
{
//cout << "Widget()" << endl;
}
Widget(const Widget&)
{
cout << "Widget(const Widget&)" << endl;
}
Widget& operator=(const Widget&)
{
cout << "Widget& operator=(const Widget&)" << endl;
return *this;
}
};
Widget f(Widget u)
{
return u;
}
int main()
{
Widget x;
Widget y;
//这里y是已经存在的对象了 (赋值拷贝构造)
y = f(x);
return 0;
}
为什么失效呢 -- 我们来画画图 分析
我们看右图 --两次拷贝构造 ,一次赋值拷贝
上面我们提到的是在传返回值时的优化,那么在什么情况下,在传参时也能够优化?
首先我们先学习匿名对象
int mian()
{
// 有名对象
Widget x;//生命周期在在main函数
//匿名对象
Widget();//生命周期只在这一行
}
我们来证明匿名对象的生命周期
调试看到,我们刚经过204匿名对象时,程序就调用了析构函数(先调构造,后调用析构),将匿名对象进行了资源的清理
我们用有名对象传参编译器不优化,那我们来看看匿名对象传参是否有优化
匿名对象传参
int main()
{
// 有名对象
//Widget x;//生命周期在在main函数
//匿名对象
//Widget();//生命周期只在这一行
f(Widget());
return 0;
}
仅仅调用了一次拷贝构造
编译器在这一块也做了优化
仅仅调用的一次拷贝构造函数还是在传返回值时调用的,在传参时并没有调用拷贝构造
看看下面代码,一共调用了多少次拷贝构造
class Widget
{
public:
Widget()
{
//cout << "Widget()" << endl;
}
Widget(const Widget&)
{
cout << "Widget(const Widget&)" << endl;
}
Widget& operator=(const Widget&)
{
cout << "Widget& operator=(const Widget&)" << endl;
return *this;
}
~Widget()
{
//cout << "~Widget()" << endl;
}
};
Widget f(Widget u)
{
Widget v(u);
Widget w = v;
return u;
}
int main()
{
Widget x;
Widget y = f(f(x));
return 0;
}
一样的画图,一步一步分析(慢就是快)
所以编译器不优化,调用9次拷贝构造函数,编译器优化后调用7次
总结上面的内容 :
优化的时机 : 传参或传返回值过程中,存在连续的构造、拷贝构造、就会被优化
(但是优化取决于编译器)