第二章:构造函数语义学:默认构造函数
记录一下第二章的第一部分默认构造函数的学习笔记:
一、默认构造函数
1.默认构造函数的生成:
概念上只要类未定义任何构造函数并且真正需要默认构造函数(被调用)时,则编译器会自动为其合成一个默认构造函数。而实际上编译器只为有意义nontrival的类合成构造函数。而无意义的类编译器为其合成无意义的默认构造函数或者根本不会合成出来。四种被定义为trivial的类①有一个具有默认构造函数的成员类对象②派生自具有默认构造函数的基类③具有虚函数④具有虚基类的类
2.构造函数扩张:
①当自定义类满足以下四个条件①类存在类成员变量②存在自定义构造函数③类成员变量存在默认构造函数④自定义构造函数函数未显示初始化类成员变量时,编译器将自动对每一个构造函数进行扩张,将调用类成员变量的默认构造函数的代码添加进自定义构造函数。、
1 #include <iostream> 2 struct A 3 { 4 A(int x):ival(x){} 5 private: 6 int ival; 7 }; 8 9 struct C 10 { 11 C(int x=1):ival(x){std::cout<<ival<<std::endl;} 12 private: 13 int ival; 14 }; 15 struct B 16 { 17 B(int x):a(1),dval(1.0){} 18 private: 19 A a; 20 C c; 21 double dval; 22 }; 23 int main() 24 { 25 B b(1); 26 } ~
②当类含有虚函数时,编译器会自动合成虚函数表vptr然后在内存中加入指向虚函数表的指针并且改写相应虚调用的代码。见书p45
class Widget { public: virtual void flip()=0; //... }; //扩张前 void flip(const Widget& widget) {widget.flip();} //扩张后 void flip(const Widget& widget) {widget.vptr[1])(&widget)
③当类含有虚继承时,编译器会对代码进行扩张,使其虚基类在执行期能够被安全使用。
因为普通继承时,在内存中按声明顺序存放相应的成员(基类子对象的顺序不做强制规定),但是虚继承要求公共基类只存在一份,并且由于多态性不能确定执行时具体调用对象是哪个,所以不能确定公共基类在内存中的具体偏移量(要为所有可能的派生类都准备一份公共基类的子对象)。所以需要对虚基类进行代码扩张,有些编译器的做法是在每个派生类中加入指向公共基类的指针,但是公共基类的对象仅有一个。
3.合成的默认构造函数中,只有成员类对象和基类的子对象和某些静态成员变量会被初始化(调用其默认构造函数)。其余非静态成员变量的值均是未定义的(压根就没合成可能)。