最近在研读《insight C++ object model》,看到第二章中构造函数部分,深有感触,故此写下,方便大家。当然,也方便我,免得我忘了。
1 class test{ 2 public: 3 int a; 4 }; 5 int main(){ 6 test t; 7 std::cout<<t.a<<std::endl; 8 }
结果大家都知道,t.a没有被初始化,所以是个随机值。那么问题来了,以以前的知识来看,当某个类中没有构造函数,编译器会为其生成一个默认构造函数,而这个默认构造函数,会调用成员变量的默认构造函数,int的默认构造函数是将其初始化为0。
1 int t=int(); //t==0
那么为什么会出现这种结果?当年的标准是这样定的:只有在编译器需要的时候才会生成默认构造函数。换句话说,编译器不关心用户的“鸟事”,编译器不在乎用户是否需要,他只关心自己。。。试试下面的代码:
test t=test(); std::cout<<t.a<<std::endl; //get 0
这时候,显式调用了test的构造函数,但test没有构造函数,编译器这时候需要构造函数来,于是他就自造了一个。于是a就得到默认值了。
为什么要有这样不合理的设计呢?Lippman在书里没有提到。但我猜想应该是为了兼容C代码,考虑一下:
1 struct test{ 2 int a; 3 }; 4 int main(){ 5 struct test t; 6 printf("%d\n",t.a); 7 }
若test的默认构造函数产生,并导致a被初始化为0,那么C++中就将输出0。但C中没有构造函数这一概念,所以就将输出任意值。早期C++是目地之一为了很好的兼容C代码,所以C++编译器为了更好的兼容C,就选择了这种看似很不可思议的方法。(ps:以鄙人之见,应该没多少人会在参数未初始化情况下直接使用,所以不兼容的概率应该及其低)
那么在什么情况下,C++编译器才会类为其产生默认构造函数呢?很简单,书上说了四种情况:
1、成员对象有默认构造函数的情况
2、此类发生继承,而基类有默认构造函数
3、此类有虚函数
4、此类虚继承自其他类
我归结一下,就是C代码里不可能出现的情况。在C代码里可能出现的情况,编译器都不会产生默认构造函数。以下依次说说这四种情况。
先考虑第一种:
class test1{ public: int a; }; class test2{ public: test1 t; int b; }; int main(){ test2 t; std::cout<<t.t.a<<std::endl; //print 0 std::cout<<t.b<<std::endl; //uninitialized }
说好的初始化呢?为什么只有t会被初始化,b怎么不会?请注意,被合成的默认构造函数只满足编译器的需要,而不是程序的需要(selfish)。换句话说,他只初始化有默认构造函数的成员对象(ps:int至少不是对象。另外,有人会问为什么不初始化有构造函数但不是默认构造函数的成员对象。。。想想,这种构造函数得被显示调用啊)。而且是一定会初始化有默认构造函数的成员对象。也就是说,
class test2{ public: //... test2(){ b=0; } };
即使如此,t也会被初始化,这段代码相当于:
class test2{ public: //... test2():test1(){ b=0; } };
未完待更