1. c++临时对象的引入
直接主题,上代码,
#include <iostream>
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的成员变量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;
}
运行结果:
还能通过cls(5)去调用成员函数:
cls(5).show();
运行结果:
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对象销毁所致
实际运行结果:
若依上面我猜测的程序执行流程运行程序,可见c1对象的产生需要经过两个构造函数:带参构造函数(临时对象调用)和拷贝构造函数(赋值类对象用),比起构造函数一次调用,显然效率低下得多,特别是在构造函数需要实现较多逻辑功能的时候。所以编译器做了优化,摈弃了临时对象的产生。优化后的代码应为:
源代码:cls c1 = cls(6);
编译器优化后:cls c1 = 6;
所以运行结果如上图所示。
回到一开始的问题,类中
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;
}
};
这样就可以避免临时对象的产生了。