C++ 临时对象

导语
函数不要返回引用类型的临时对象,也不要引用函数返回的临时对象,因为无法预测会发生什么。

一、引用和指针的区别

引用

引用声明时,需要初始化

引用是被引用对象的别名,和被引用对象绑定后不可变更

引用一定不为空,不需要判定是否空对象

使用引用传递,可以避免临时对象

指针

指针可以多次赋值,改变指向的对象

指针可以指向空

二、什么是临时对象

临时对象是在源码中不可见的,是栈上的、没有名字的对象

三、临时对象产生的原因

类型不匹配:一般情况是指当需要X类型的对象时提供的却是其它类型的对象。编译器需要以某种方式将提供的类型转换成要求的X类型。这一过程可能会产生临时对象。(为使函数调用成功而进行隐式类型转换)

按值传递:创建和销毁临时对象的代价是比较高的。倘若可以,我们应该按指针或者引用来传递对象以避免生成临时对象。

按值返回:如果编写的函数是按值返回对象(与引用或者指针相对),就很可能生成临时对象。(函数返回值)

临时对象案例:

1
2
3
4
5
void conv(string str){  ...  } 

int main(){
conv("abcd");
}

考虑以上代码

abcd并不是直接传给str,而是先传递给临时对象,再由临时对象传递给形参str,因为他们俩的类型不一样

“abcd”(字符常量是 const 类型)—传递—> 临时对象(默认const)—传递—> 变量 str (非 const)

如果每次调用都产生临时对象,则会增加消耗

可以考虑用引用传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void conv(string& str){ ... }

int main()
{
conv("abcd");
// 会报错,cannot bind non-const lvalue reference of type 'std::string&'to an rvalue of type 'std::string'
// 不能绑定一个 非const类型的左值引用(str) 到一个右值上
}

或者 加const,但是不可在函数中修改了

void conv(const string& str){ ... }

int main()
{
conv("abcd");
}

或者

重要知识点,使用非const类型的引用时,参数只能是相同类型,而const类型可以实现传递与此类型相关的参数进来。
(不能把const 类型转换为非const,但是可以把 非const 当做const类型用)
将 "std::string &" 类型的引用绑定到 "const std::string" 类型的初始值设定项时,限定符被丢弃

void conv(string& str)
{
str = "adsfsa";
cout << str << endl;
}

int main(){

string str1 = "abcd";
conv(str1);
return 0;
}

函数返回值产生的临时对象

返回值 —传递—> 临时对象 —传递—> 主函数

四、总结

可以使用operator=()消除临时对象。

临时对象会以构造函数和析构函数的形式降低一半的性能。

将构造函数声明为explicit,可以阻止编译器在幕后使用类型转换。

编译器常常创建临时对象来解决类型不匹配问题。通过函数重载可以避免这种情况。

如果可能,应尽量避免使用对象拷贝。按引用传递和返回对象。

可能是”+、-、*”或者”/”的地方,使用 = 运算符可以消除临时对象。

不要试图使用”引用”来减少构造和析构函数的调用。如果你非要优化的话,建议使用返回值优化(RVO)。

1
2
3
4
Cat foo2()
{
return Cat("b");
}

如此一来,main函数中的变量c就不会调用拷贝构造了。

------------- 感谢您的阅读-------------
作者dreamingpoet
有问题请发邮箱 Dreamingoet@126.com
您的鼓励将成为创作者的动力