导语
函数不要返回引用类型的临时对象,也不要引用函数返回的临时对象,因为无法预测会发生什么。
一、引用和指针的区别
引用
引用声明时,需要初始化
引用是被引用对象的别名,和被引用对象绑定后不可变更
引用一定不为空,不需要判定是否空对象
使用引用传递,可以避免临时对象
指针
指针可以多次赋值,改变指向的对象
指针可以指向空
二、什么是临时对象
临时对象是在源码中不可见的,是栈上的、没有名字的对象
三、临时对象产生的原因
类型不匹配:一般情况是指当需要X类型的对象时提供的却是其它类型的对象。编译器需要以某种方式将提供的类型转换成要求的X类型。这一过程可能会产生临时对象。(为使函数调用成功而进行隐式类型转换)
按值传递:创建和销毁临时对象的代价是比较高的。倘若可以,我们应该按指针或者引用来传递对象以避免生成临时对象。
按值返回:如果编写的函数是按值返回对象(与引用或者指针相对),就很可能生成临时对象。(函数返回值)
临时对象案例:
1 | void conv(string str){ ... } |
考虑以上代码
abcd并不是直接传给str,而是先传递给临时对象,再由临时对象传递给形参str,因为他们俩的类型不一样
“abcd”(字符常量是 const 类型)—传递—> 临时对象(默认const)—传递—> 变量 str (非 const)
如果每次调用都产生临时对象,则会增加消耗
可以考虑用引用传递参数
1 | void conv(string& str){ ... } |
函数返回值产生的临时对象
返回值 —传递—> 临时对象 —传递—> 主函数
四、总结
可以使用operator=()消除临时对象。
临时对象会以构造函数和析构函数的形式降低一半的性能。
将构造函数声明为explicit,可以阻止编译器在幕后使用类型转换。
编译器常常创建临时对象来解决类型不匹配问题。通过函数重载可以避免这种情况。
如果可能,应尽量避免使用对象拷贝。按引用传递和返回对象。
在
不要试图使用”引用”来减少构造和析构函数的调用。如果你非要优化的话,建议使用返回值优化(RVO)。
1 | Cat foo2() |
如此一来,main函数中的变量c就不会调用拷贝构造了。