临时对象是栈上的没有名字的对象,它在源码中是不可见的,是编译器偷偷摸摸在背后干的。
比如下面的代码就会产生临时对象:
int i = 1;
// 这里的 i++ 会产生临时对象,这里的临时对象是在系统中产生,代码中看不见的
// 首先将i的值赋给临时对象,再把临时对象的值作为返回结果赋给j,再对i进行自增操作。
int j = i++;
临时对象有以下几个特点:
(1)临时对象是在栈上生成的。
(2)临时对象是右值,可通过“const 左值引用”绑定临时对象,或通过右值绑定临时对象。
(3)临时对象的产生和销毁会调用1次构造函数和析构函数。
临时对象的产生和销毁需要消耗系统资源,所以在写代码时应尽量避免临时对象的产生。
1、临时对象的产生及解决办法
临时对象主要产生在下列场景中:
(1)在栈中生成无名对象时
Integer objI = Integer(1);
先产生一个Integer临时对象,然后调用拷贝构造函数生成objI对象。
解决办法:在定义时直接初始化。
Integer objI(1);
Integer objI{1};
(2)类型转换
class MyDemo {
public:
MyDemo(){}
MyDemo(int i) {
std::cout << "构造函数MyDemo(int i)" << std::endl;
}
MyDemo& operator = (const MyDemo& _demo) {
std::cout << "赋值运算符" << std::endl;
return *this;
}
~MyDemo() {
std::cout << "析构函数" << std::endl;
}
};
int main()
{
MyDemo demo;
demo = 100;
return 0;
}
这里的demo = 100,会产生临时对象:
(1)将 100为参数调用了MyDemo的构造函数创建了一个临时对象;
(2)通过拷贝赋值运算符,将临时对象的成员值赋给了demo;
(3)销毁临时对象。
解决办法:创建对象时进行初始化。
MyDemo demo = 100;
(3)隐式类型转换
void testFunc(const MyDemo& demo) {}
int main()
{
int i = 2;
testFunc(i);
return 0;
}
上面的testFunc(i),会产生临时对象。int值会先转换成MyDemo类型的临时对象,然后赋值给形参demo。
解决办法:
将传入的实参和函数的形参类型保持一致即可:
testFunc(MyDemo(i));
(4)函数参数
函数的参数传值是值传递,在函数调用时系统会创建一个临时对象。
class MyDemo {
public:
MyDemo() {}
MyDemo(int i) {
std::cout << " 构造函数MyDemo(int i)" << std::endl;
}
MyDemo(const MyDemo& _demo) {
std::cout << " 拷贝构造函数" << std::endl;
}
~MyDemo() {
std::cout << " 析构函数" << std::endl;
}
};
void testFunc(MyDemo demo) {
std::cout << " testFunc" << std::endl;
}
int main()
{
MyDemo demo;
testFunc(demo);
return 0;
}
从执行结果可以看到,在调用testFunc()函数时,指向了拷贝构造函数。
解决办法:把值传递改成引用传递。
void testFunc(MyDemo& demo) {}
(5)函数返回值
在函数返回对象时候,会创建一个临时对象接收这个对象;从而调用了拷贝构造函数,和析构函数。
MyDemo testFunc() {
MyDemo demo;
return demo;
}
int main()
{
MyDemo demo = testFunc();
return 0;
}
解决办法:
(1)修改testFunc的形参,把返回对象作为形参传入函数。
MyDemo testFunc(MyDemo& ret) {}
(2)用右值接收返回值
MyDemo&& d = testFunc();