C中最值得警惕的是野指针,C++最值得的就是野指针和临时对象了,临时对象是C++中的一个灰色地带了,那么它是如何产生的呢,答案是由于直接调用构造函数导致的。
直接调用构造函数将产生一个临时对象,它的生命周期只有一条语句的时间,作用域也仅仅在一条语句中,当语句执行完就自动被析构了。我们是看不见临时变量的,也没有名称,但是它却实际存在着,并影响着程序运行效率的,。我们需要其来源并清楚如何避免临时变量产生。
class Test {
int i;
public:
Test(int ret) {
i = ret;
}
Test() {
Test(0);
}
void print() {
printf("i = %d\n", i);
}
};
在这个类里面有两个构造函数,一个是带参构造函数,另一个是无参构造函数,在无参构造函数里又调用了有参构造函数。按我们本意是用0来初始化类成员变量,当我们在main里运行下面两行代码:
Test t;
t.print();
结果并没有在我们的意料之内,而是输出的尽是随机值,这就是临时变量惹的祸,由于临时变量作用域和生命周期都只在一条一句上有效,即调用构造函数本身这条语句,所以无参构造函数的函数体和空函数并没有什么区别,这也是为什么打印的是随机值了,一来并没有初始化,二来是局部类对象。
看下面一条语句:
Test t = Test(10);
创建一个类对象时调用构造函数来初始化,对于这条语句我们可以分解成:1、直接调用一个构造函数生成了一个临时变量2、将临时变量赋值给新创建的对象。由于没有调用系统资源,所以可以使用浅拷贝而不会出错。但是当我们自定义一个拷贝构造函数来做实验查看拷贝构造函数是否被调用时却发现程序并没有调用拷贝构造函数,这又是为什么?因为编译器也发现这是个灰色地带,所以他也会尽量的在不影响最终结果的前提下优化代码致使临时变量减少。即等价于Teas t = 10;
Test func()
{
return Test(20);
}
...
...
...
Test tt = func();
这也是容易产生临时变量的一种行为,代码等价于Test tt = Test(20);所以也等价于Test tt = 20;所以编译器最终直接优化成了Test tt = 20;
所以我们应该避免临时变量的产生,只有编译器帮忙还不够,我们写代码的人也需要在设计和使用上避免临时变量的产生。
拷贝构造函数中的参数是引用,因为不是引用就会无限制的调用拷贝构造函数,所以编译器强制报错提醒我们必须使用引用,引用传递是地址传递,和指针一样还发现一个事实,当拷贝构造函数参数没有const时无法通过临时对象创建新对象。
Func()返回的是一个编译器构造的临时变量,而编译器默认 " 临时变量只能是const引用",因此如果拷贝构造函数不加const,调用Test的构造函数的时候,将无法把形参的引用绑定在 Func()返回的临时变量上面,也就是说无法匹配构造函数Test::Test(Test& obj),临时对象的引用只能是const的,因此很多函数引用形参都带const。