持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情
本文主要总结了在C++开发过程中所遇到的临时对象产生的相关问题,三种常见的临时对象创建的情况:
类型转换
函数传对象
函数返回对象
类的结构
#include <iostream>
using namespace std;
class A
{
public:
int a;
int b;
public:
A(int t1 = 0,int t2 = 0){
a = t1;
b = t2;
cout << "类A的有参构造被调用" << endl;
}
~A(){
cout << "类A的析构函数被调用" << endl;
}
A(const A& tmp){
cout << "类A的拷贝构造函数被调用" << endl;
a = tmp.a;
b = tmp.b;
cout << "this:" <<this<< endl;
}
A& operator=(const A& tmp){
cout << "类A的拷贝赋值运算符" << endl;
a = tmp.a;
b = tmp.b;
cout << "this:" <<this<< endl;
return *this;
}
};
类型转换
int main()
{
cout << "-----------" << endl;
A peach;
peach = 100;
cout << "peach的地址:" <<&peach<< endl;
cout << "-----------" << endl;
return 0;
}
/*
-----------
类A的有参构造被调用
类A的有参构造被调用
类A的拷贝赋值运算符
this:0x7ffedafcf978
类A的析构函数被调用
peach的地址:0x7ffedafcf978
-----------
类A的析构函数被调用
*/
peach = 100;发生了隐式类型转换,将100来构造一个A对象
用explicit来禁止隐式类型转换
将100赋给peach,而peach本身是个A类型的对象,100是个数字,编译器帮助我们以100为参数调用了类A的构造函数创建了一个临时对象,因为A构造函数的两个参数都有默认值,所以这里的100就代替了第一个参数,所以从100是可以成功创建出A类对象的。
产生临时对象需要花时间,耗费性能,我们要避免产生临时对象。
改进
int main()
{
cout << "1-----" << endl;
A peach = 100;
cout << "peach的地址:" <<&peach<< endl;
cout << "-----------" << endl;
cout << "2-----" << endl;
A peach1(100);
cout << "peach1的地址:" <<&peach1<< endl;
cout << "-----------" << endl;
cout << "3-----" << endl;
A peach2(peach1);
cout << "peach2的地址:" <<&peach2<< endl;
cout << "-----------" << endl;
return 0;
}
/*
1-----
类A的有参构造被调用
peach的地址:0x7fff9db66340
-----------
2-----
类A的有参构造被调用
peach1的地址:0x7fff9db66348
-----------
3-----
类A的拷贝构造函数被调用
this:0x7fff9db66350
peach2的地址:0x7fff9db66350
-----------
类A的析构函数被调用
类A的析构函数被调用
类A的析构函数被调用
*/
A peach = 100;创建一个名叫peach的A类型的房子,直接让100住进去,不会产生临时对象
A peach1(100);同上
A peach2(peach1);将peach1拷贝一份赋值给peach2
函数传对象
1、按值传递
按值传递时,首先将需要传给函数的参数,调用拷贝构造函数创建一个副本,所有在函数里的操作都是针对这个副本,也正是因为这个原因,在函数体力对该副本进行任何操作都不会影响原参数。
void func1(A temp){
temp.a = 999;
cout << "func1函数中tmpe对象a的值" <<temp.a<< endl;
cout << "func1函数中tmpe对象的地址" <<&temp<< endl;
}
int main()
{
A apple(20,30);
func1(apple);
cout << "-------" << endl;
cout << "apple对象a的值" <<apple.a<< endl;
cout << "apple:" <<&apple<< endl;
return 0;
}
/* 类A的有参构造被调用 类A的拷贝构造函数被调用 this:0x7ffe65b8bc30 func1函数中tmpe对象a的值999 func1函数中tmpe对象的地址0x7ffe65b8bc30
类A的析构函数被调用
apple对象a的值20 apple:0x7ffe65b8bc28 类A的析构函数被调用 */
拷贝构造一个apple的副本,传给temp;副本和temp地址为0x7ffe65b8bc30
apple地址为0x7ffe65b8bc28
有临时对象产生,析构函数被调用2次
2、按引用传递(改进)
引用则不然,它是对象本身,只是一个别名而已
void func1(A& temp){
temp.a = 999;
cout << "func1函数中tmpe对象a的值" <<temp.a<< endl;
cout << "func1函数中tmpe对象的地址" <<&temp<< endl;
}
int main()
{
A apple(20,30);
func1(apple);
cout << "-------" << endl;
cout << "apple对象a的值" <<apple.a<< endl;
cout << "apple:" <<&apple<< endl;
return 0;
}
/*
类A的有参构造被调用
func1函数中tmpe对象a的值999
func1函数中tmpe对象的地址0x7ffc02af5c00
-------
apple对象a的值999
apple:0x7ffc02af5c00
类A的析构函数被调用
*/
给apple起个别名叫temp,temp和apple的地址都是0x7ffc02af5c00。
apple地址为0x7ffe65b8bc28
无临时对象产生,析构函数被调用1次
3、按地址传递(改进)
void func1(A* temp){
(*temp).a = 999;
cout << "func1函数中tmpe对象a的值" <<(*temp).a<< endl;
cout << "func1函数中tmpe对象的地址" <<temp<< endl;
}
int main()
{
A apple(20,30);
func1(&apple);
cout << "-------" << endl;
cout << "apple对象a的值" <<apple.a<< endl;
cout << "apple:" <<&apple<< endl;
return 0;
}
/*
类A的有参构造被调用
func1函数中tmpe对象a的值999
func1函数中tmpe对象的地址0x7ffc8b8a1180
-------
apple对象a的值999
apple:0x7ffc8b8a1180
类A的析构函数被调用
*/
直接传地址进去,肯定无新对象产生,但不如引用方便
无临时对象产生,析构函数被调用1次
函数返回对象
A func1(const A& temp){
cout << "func1函数中temp对象的地址" <<&temp<< endl;
A peach = {temp.a+100,temp.b+100};
cout << "func1函数中peach对象的地址" <<&peach<< endl;
return peach;
}
int main()
{
A apple(20,30);
cout << "main函数中apple对象的地址" <<&apple<< endl;
A t1;
cout << "main函数中t1对象的地址" <<&t1<< endl;
t1 = func1(apple);
cout << "main函数中t1对象的地址" <<&t1<< endl;
return 0;
}
/* 类A的有参构造被调用 main函数中apple对象的地址0x7ffd86f6db60 类A的有参构造被调用 main函数中t1对象的地址0x7ffd86f6db68 func1函数中temp对象的地址0x7ffd86f6db60 类A的有参构造被调用 func1函数中peach对象的地址0x7ffd86f6db70 类A的拷贝赋值运算符 this:0x7ffd86f6db68 类A的析构函数被调用 main函数中t1对象的地址0x7ffd86f6db68 类A的析构函数被调用 类A的析构函数被调用 */
改进
A func1(const A& temp){
cout << "func1函数中temp对象的地址" <<&temp<< endl;
A peach = {temp.a+100,temp.b+100};
cout << "func1函数中peach对象的地址" <<&peach<< endl;
return peach;
}
int main()
{
A apple(20,30);
cout << "main函数中apple对象的地址" <<&apple<< endl;
A t1 = func1(apple);
cout << "main函数中t1对象的地址" <<&t1<< endl;
return 0;
}
/* 类A的有参构造被调用 main函数中apple对象的地址0x7ffcc1692458 func1函数中temp对象的地址0x7ffcc1692458 类A的有参构造被调用 func1函数中peach对象的地址0x7ffcc1692460 main函数中t1对象的地址0x7ffcc1692460 类A的析构函数被调用 类A的析构函数被调用 */
错误
A & func1(const A& temp){
cout << "func1函数中temp对象的地址" <<&temp<< endl;
A peach = {temp.a+100,temp.b+100};
cout << "func1函数中peach对象的地址" <<&peach<< endl;
return peach;
}
int main()
{
A apple(20,30);
cout << "main函数中apple对象的地址" <<&apple<< endl;
A t1 = func1(apple);
cout << "main函数中t1对象的地址" <<&t1<< endl;
return 0;
}
注意,如果返回对象是引用的话,在函数里创建的局部对象,在返回时就被销毁了,这时若再引用该对象就会产生未知错误。