什么是临时对象?
C++真正的临时对象是不可见的匿名对象,不会出现在你的源码中,但是程序在运行时确实生成了这样的对象。
通常出现在以下两种情况:
(1)为了使函数调用成功而进行隐式类型转换的时候。
传递某对象给一个函数,而其类型与函数的形参类型不同时,如果可以通过隐式转化的话可以使函数调用成功,那么此时会通过构造函数生成一个临时对象,当函数返回时临时对象即自动销毁。如下例:
//计算字符ch在字符串str中出现的次数
int countChar (const string& str, char ch);char buffer[];
char c;
//调用上面的函数
countChar (buffer, c);
我们看的第一个参数为char[],而函数的参数类型为const string&,参数不一致,看看能否进行隐式转化,string类有个构造函数是可以作为隐式转化函数(参见5)的。那么编译器会产生一个string的临时变量,以buffer为参数进行构造,那么countChar中的str参数会绑定到此临时变量上,直到函数返回时销毁。
注意这样的转化只会出现在两种情况下:函数参数以传值(by value)的方式传递 或者 对象被传递到一个 reference-to-const 参数上。
传值方式:
int countChar (string str, char ch);string buffer;
char c;
//参数通过传值方式传递
countChar (buffer, c);
这种方法会调用string的拷贝构造函数生成一个临时变量,再将这个临时变量绑定到str上,函数返回时进行销毁。
传常量引用:
开始的实例即时属于这种情况,但一定强调的是传递的是const型引用,如将开始函数的原型改为
int countChar (string& str, char ch);
下面调用相同,编译器会报错!为什么C++设计时要求 当对象传递给一个reference-to-non-const 参数不会发生隐式类型转化呢?
下面的实例可能向你说明这样设计的目的:
//声明一个将str中字符全部转化为大写
void toUpper (string& str);char buffer[] = "hazirguo";toUpper(buffer); //error!!非const引用传递参数不能完成隐式转化
如果编译器允许上面的传递完成,那么,会生成一个临时对象,toUpper函数将临时变量的字符转化为大写,返回是销毁对象,但是对buffer内容毫无影响!程序设计的目地是期望对“非临时对象”进行修改,而如果对reference-to-non-cosnt对象进行转化,函数只会对临时变量进行修改。这就是为什么C++中要禁止non-const-reference参数产生临时变量的原因了。
(2)当函数返回对象的时候。
当函数返回一个对象时,编译器会生成一个临时对象返回,如声明一个函数用来合并两个字符串:
const string strMerge (const string s1, const string s2);
大多时候是无法避免这样的临时变量产生的,但是现代编译器可以将这样的临时变量进行优化掉,这样的优化策略中,有个所谓的“返回值优化”,下一篇具体讲解。
总结:
临时对象有构造和析构的成本,影响程序的效率,因此尽可能地消除它们。而更为重要的是很快地发现什么地方会生成临时对象:
- 当我们看到一个reference-to-const参数时,极可能一个临时对象绑定到该参数上;
- 当我们看到函数返回一个对象时,就会产生临时对象。
参考文献: 《More Effective C++ 35个改善编程与设计的有效方法 中文版》