有趣的问题:
我们在无参构造函数中调用带一个参数的构造函数。
程序如下:
1 #include <stdio.h> 2 3 class Test { 4 int mi; 5 public: 6 Test(int i) { 7 mi = i; 8 } 9 Test() { 10 Test(0); 11 } 12 void print() { 13 printf("mi = %d ", mi); 14 } 15 }; 16 17 18 int main() 19 { 20 Test t; 21 22 t.print(); 23 24 return 0; 25 }
运行结果如下:
mi并没有打印出0,而是一个随机值。
发生了什么呢?
思考:
构造函数可以直接手工调用。
上面的程序第10行直接调用了构造函数产生了临时对象。它的生命期只有第10行这一条语句,过了这条语句就会被析构。
而且这个临时对象没有名字,作用域也仅仅在第10行这条语句中。第10行这条语句等价于没有,也就是空语句,因为这个临时对象我们无法使用。
我们在无参构造函数中调用有参构造函数的本意是代码复用,但是没有达到目的,在工程中,代码复用时,我们要在类中提供一个普通的init函数,这时就可以将公共代码写到这个函数中。
临时对象示例:
由临时对象直接调用成员函数:
可以看到临时对象构造完之后立即调用了打印函数。
工程中要避免临时对象的产生与使用。
编译器的行为:
现代编译器在不影响最终执行结果的前提下,会尽力减少临时对象的产生。
示例如下:
1 #include <stdio.h> 2 3 class Test 4 { 5 int mi; 6 public: 7 Test(int i) 8 { 9 printf("Test(int i) : %d ", i); 10 mi = i; 11 } 12 Test(const Test& t) 13 { 14 printf("Test(const Test& t) : %d ", t.mi); 15 mi = t.mi; 16 } 17 Test() 18 { 19 printf("Test() "); 20 mi = 0; 21 } 22 int print() 23 { 24 printf("mi = %d ", mi); 25 } 26 ~Test() 27 { 28 printf("~Test() "); 29 } 30 }; 31 32 Test func() 33 { 34 return Test(20); 35 } 36 37 int main() 38 { 39 Test t = Test(10); // ==> Test t = 10; 40 Test tt = func(); // ==> Test tt = Test(20); ==> Test tt = 20; 41 42 t.print(); 43 tt.print(); 44 45 return 0; 46 }
39行的Test t = 10和Test t = Test(10)的编译运行结果是一样的。
Test t = Test(10)在我们的直觉判断会有以下两步,1、创建临时对象;2、用临时对象初始化t(调用拷贝构造函数)。
但是程序执行结果并不是按照我们的直觉分析那样的,这就是因为编译器做了优化。编译器会尽力减少临时对象的产生。
编译器将Test t = Test(10)等价成了Test t = 10的形式。
临时对象的产生会带来性能上面问题。
工程中的构造函数会非常复杂,调用一次构造函数耗时很长。
上述程序的运行结果如下:
32-35行返回临时对象,然后在40行用这个对象初始化tt,但是从打印结果可以看出,构造函数并不是按照我们想象的情形来调用的,我们想象的情形是先调用构造函数产生临时对象,然后调用拷贝构造函数初始化tt。运行结果没有调用拷贝构造函数。
这也是因为编译器的优化,在不影响执行结果的前提下会极力的减少临时对象的产生。
我们写程序也应该尽力的避开临时对象,例如程序写成Test t = 10,而不是Test t = Test(10)。这样就可以保证在任何一款编译器下都不会产生临时对象。
小结: