本章主要介绍类的构造函数有关的语义(semantics),重点讨论:
1.什么情况下需要构造函数/拷贝构造函数?
2.构造函数中编译器对类的某些特性的必要处理。
1.默认构造函数
1.1Member Class Object with Default Constructor
如果A类内有类类型的成员变量B,而这个变量有默认构造函数,这编译器会为A合成一个默认构造函数,并将B的默认构造函数安插进去。
1.2Base Class with Default Constructor
如果类A继承自带有默认构造函数的类B,编译器同样为A合成构造函数,以执行B的默认构造函数。
1.3Class with a Virtual Function
这种情况下,为什么编译器同样需要合成默认构造函数,合成其的目的是什么?
显然是为了设置虚函数表及虚函数指针,在默认构造函数里做这些事情再合适不过了,编译器设计者就是这样做的。
1.4Class whit a Virtual Base Class
虚继承解决了多重继承下可能包含多个相同的基类的问题。为什么虚继承也需要生成默认构造函数?作用是什么?
事实上,这时构造函数需要将指向虚基类的指针安插到派生类中,使其能够通过指针索引虚基类的成员。具体可参考书3.4节。
2.拷贝构造函数(copy constructor)
编译器需要为一个类合成一个拷贝构造函数的情况和默认构造函数类似,需要合成拷贝构造函数当然是为了做某些必要的事,这些事主要和以下三个方面有关。
1.该类内部有类类型,且这个类有显式拷贝构造函数或继承的基类中有拷贝构造函数。
这时类不能按照位拷贝的方式拷贝一个类对象,可以想象,显式定义拷贝构造函数的目的往往可能就是为了避免位拷贝,编译器理应执行用户定义的拷贝构造函数。
2.类的虚函数表指针以及虚函数表。
C++类特性中,需要特别注意的就是虚函数相关的内容。为了支持多态,在派生关系中不同类的对象中的虚函数指针必须不一样,这当然需要编译器特别处理而不能简单的使用位拷贝复制其他类的虚函数表指针。
3.虚继承。
我们之前提过,虚继承由于其独一份的特殊性,在如下图的继承关系中,ReadPanda中保存的虚基类ZooAnimal的offese偏移地址和Raccoon保存的ZooAnimal的偏移地址往往不一致,当然不能执行位拷贝。
RedPanda little_red;
Raccoon little_critter = little_red;
编译器必须合成拷贝构造函数安插进相应代码以调整little_critter中虚基类的偏移量,而不是拷贝little_red中的偏移量。
总结
C++类的多种特性,结果是C++编译器在处理其支持的特性时,需要做一些额外的操作,进而带来一些性能的影响。这些特性往往和虚函数、虚继承有很大关系,在构建项目时,需要考虑你的类是否真的需要应用某些特性,避免不必要的性能损失。