一、构造函数
编译器会合成4个隐性的非无用的构造函数的情况:
1.含有带有默认构造函数的成员类的对象。
这个合成构造函数只有被真正使用时,才会被合成。这时有一个问题产生,即在多个不同的编译 块产生都各自每个的合成构造函数。如何避免多个默认构造函数产生,将合成的默认、拷贝、析 构、赋值拷贝以inline方式完成。如果,类成员有多的,编译器不管该类有没有默认构造函数,都会调用每个成员的默认构造函数,会扩张现有的构造。不会另外生成新的默认构造函数。其调用各自构造器的顺序与声明顺序一致。
2.带有默认函数的基类
如果一个没有任何构造函数的类派生自一个有默认构造函数的基类,则这个编译器生成的构造函数也是非无用。编译器会扩充现有的每个构造函数。不会另外生成新的默认构造函数。3.带有虚函数的类
a.class声明或继承一个虚函数。
在此种情况,为了保证多态机制的完成。一般,编译器会自动的为基类对象和其派生类的vptr设置初值,来放置虚表的地址。至于没有声明任何默认构造函数的类,编译器会生成,一个非无用的默认构造,来确保每一个类的vptr正确初始化。
b.class派生一个继承链,有一个或多于一个虚基类。
4.带有虚基类的类
原书中的例子,类X,A,B,C存在以下关系:
这时,foo函数的参数为基类A类型的指针,在看看main里面,foo函数调用了两次,由于泛型和多态的原因,编译器无法得到X::i的实际位置。因为pa的类型是不固定的,编译器必须使用延时的代码段,将pa的类型和i的读取重原来的编译期延时到运行期。派生类通过一个指针指向每一个虚基类。foo实际单用过程如下:
_vbcX表示编译器在构造期间完成的, 对于每一个存在虚继承类的构造函数,如果类没有默认构造函数,编译器会安插那些代码段。这时,编译器生成的构造函数是非无用的。
c++新手的两大误解:
1.任何没有定义默认构造函数,就会有编译器生成一个。(只有该类没有构造函数,才会合成一个默认构造函数。其实有时候,不是重新生成,而是扩充现有。以上4中情况)2.编译器合成的默认构造函数,会初始化类中,每一个数据成员的默认值。
二、拷贝构造
1 .按成员拷贝
当一个类没有明确的拷贝构造函数,这时编译器会合成一个拷贝构造函数。这个拷贝函数是安成员进行复制的,如书中的例子:如果一个string类对象呗声明为其他类的成员,如word类 会先拷贝_occers成员,然后安成员拷贝 _word成员。
2 .按位拷贝
3.编译器合成拷贝构造函数不是按位拷贝的4中情况(也就是生成的拷贝函数是非无用的):
1.类内部含有拷贝函数的对象成员(无论是定义的还是编译器合成的)时。
2.类的父类具有拷贝构造函数(无论是定义的还是编译器合成的)。
3.类内部有一个或多个虚函数时
4.类派生自继承 串链。
前两种情况不必多说,主要看后两种情况。
第三种情况需要重新设定虚表指针:
由于一个具有虚函数的类内部,有一个虚表指针,指向在编译期间生成虚函数表。当类的导入vptr到类中,类就不再展现按位拷贝的特性了。
三、程序转化语义学
1.明确的初始化 当连续的定义多个对象,对象的初始化会被剔除,在结束定义后,插入构造函数实现初始化。 定义对象x1, x2, X3 的过程如下:被编译器转换后的古城如下:
2.参数的初始化 3.返回值的初始化
四、成员们的初始化列表
与c++ primer中 提到的一样,必须以列表形式初始化的成员有:1.成员变量为引用。2.成员带有const属性。3.基类构造函数具有一组参数。4.成员类对象的构造函数具有一组参数。并且他们的初始化顺序只与声明顺序有关,与列表的顺序无关。这里可能的顺序,可能出现程序的漏洞,g++ gdn c++编译器6等编译器发出警告。