C/C++编程|c++类中的临时对象

1. c++临时对象的引入 直接主题,上代码,

#include class cls { public: int a; cls(int i) { a = i; }cls() { cls(0); }void show() { std::cout << "a = " << a << std::endl; } }; int main(void) { cls c; c.show(); return 0; }

程序想要实现的功能很简单:定义对象c的时候会编译器会去调用无参构造函数cls(),在cls()中以0作为参数去调用有参构造函数cls(int i),达到将成员变量a设置为0的目的。这是c++代码复用的思想,挺好,看运行结果:
C/C++编程|c++类中的临时对象
文章图片

显然,打印出来的对象c的成员变量a并没有得到初始化。为什么?这就是临时对象捣的鬼。
2. 临时对象 在c++中,直接调用(代码上手动去调用而非编译器自己调用)构造函数将会产生一个临时对象,写几句代码验证临时对象的存在:
class cls { public: int a; cls(int i) { a = i; std::cout << "cls(int i)" << std::endl; }cls() { a = 0; std::cout << "cls()" << std::endl; }cls(const cls& m)//拷贝构造函数 { std::cout << "cls(const cls& m)" << std::endl; }void show() { std::cout << "a = " << a << std::endl; }~cls() { std::cout << "~cls()" << std::endl; } }; int main(void) { cls(5).show(); //手动调用带参构造函数return 0; }

运行结果:
C/C++编程|c++类中的临时对象
文章图片

还能通过cls(5)去调用成员函数:
cls(5).show();

运行结果:
C/C++编程|c++类中的临时对象
文章图片

show()的调用便是通过临时对象,临时对象没有对象名,它的生命周期只有一条语句(手动调用构造函数)的时间,作用域也只在这条语句间。
注意,临时对象并非好东西,它是c++程序中很多BUG来源之一,c++编译器会在不影响程序最终执行效果的前提下,尽量减少临时对象的产生。
代码验证:
int main(void) { //cls(5).show(); cls c1 = cls(6); return 0; }

程序中,cls(6)很明显会产生一个临时对象,并将该临时对象赋值给c1类。这里的赋值操作自然会调用到拷贝构造函数,所以猜测程序的运行结果为:
cls(int i)//cls(6)所致 cls(const cls& m)//=所致 ~cls()//临时对象销毁所致 ~cls()//c1对象销毁所致

实际运行结果:
C/C++编程|c++类中的临时对象
文章图片

若依上面我猜测的程序执行流程运行程序,可见c1对象的产生需要经过两个构造函数:带参构造函数(临时对象调用)和拷贝构造函数(赋值类对象用),比起构造函数一次调用,显然效率低下得多,特别是在构造函数需要实现较多逻辑功能的时候。所以编译器做了优化,摈弃了临时对象的产生。优化后的代码应为:
源代码:cls c1 = cls(6); 编译器优化后:cls c1 = 6;

所以运行结果如上图所示。
【C/C++编程|c++类中的临时对象】回到一开始的问题,类中
cls() { cls(0); }

事实上,cls(0)会生成一个临时对象,而0是初始化这个临时对象的。该行代码执行过后,临时对象便被销毁,所以不能实现程序预先的目的。在实际中,若确实要实现代码复用的功能,可以写成:
class cls { public: int a; void init(int b) { a = b; }cls(int i) { init(i); std::cout << "cls(int i)" << std::endl; }cls() { init(a); std::cout << "cls()" << std::endl; }cls(const cls& m)//拷贝构造函数 { std::cout << "cls(const cls& m)" << std::endl; }void show() { std::cout << "a = " << a << std::endl; }~cls() { std::cout << "~cls()" << std::endl; } };

这样就可以避免临时对象的产生了。

    推荐阅读