构造函数 ######################################## 默认构造函数 **************************************** C++在类的构造中有以下确定行为: #. 当一个类作为数据成员时,其一定会被初始化 #. 当内置类型(int、double、char、数组、指针)作为数据成员时,默认不进行初始化 #. 当一个类中含有虚函数,或者类继承自一个继承单链,继承了基类的虚函数。则虚函数表指针(vptr)将会被初始化 #. 当一个类继承自虚有基类时,编译器可能会在基类中插入一个指针用于对基类进行存取操作,该指针将被初始化。 #. 基类构造函数必定会被执行。 .. note:: 当继承自虚有基类时,更多的解释如下: 考虑一下代码: .. code-block:: cpp class X{ public: int i = 10; }; class A : virtual public X{}; class B : virtual public X{}; class C: public A, public B{}; void func(const A* pa){ pa->i = 1024; } 其继承结构如下: .. image:: assets/虚有继承.png 由于类型 A 的实际类型可变,编译器需要改变 **执行存取操作的代码** ,导致 X::i 的实际偏移位置在内存中可能在运行时才能确定。cfront 最初的做法在派生类中的每一个虚有基类中插入一个指针,所有 **对基类执行存取的操作** 都可以通过该指针完成,此时, ``pa->i`` 可能被改写为:``pa->_vbcX->i = 1024`` 。 其中 ``_vbcX`` 是由编译器生成的、指向虚有基类 X 的指针。该指针必定会被初始化。 为了完成以上任务,编译器在编译器可能做出以下行为: #. 当类不提供构造函数时,合成默认构造函数用于执行初始化代码 #. 当类提供了构造函数,但是没有提供默认构造函数时,编译器将会在每个构造函数前加入初始化代码。 .. note:: - 在合成默认构造函数中,将会调用类成员的默认构造函数对类成员进行构造,``类成员的初始化顺序与其声明顺序相同`` 。 - 若数据成员全为内置类型,虽然编译器会合成默认构造函数,但是该默认构造函数不会执行任何操作。 - 所谓的 ``初始化代码`` 包括:初始化类成员、vptr、用于对基类执行存取操作的指针、基类的构造函数。以上代码仅在未显式初始化时才会由编译器生成 拷贝构造函数 **************************************** 编译器在类处于以下情形时将会合成拷贝构造函数: - 数据成员中包含类成员 - 若基类含有拷贝构造函数,则子类会合成拷贝构造函数 - 类中含有虚函数 - 类派生自一个包含虚有基类的继承链 之所以虚函数与拷贝构造函数相关,是因为对象在被拷贝时必须要重新设定vptr(而不能简单地将vptr的内容拷贝到另一个类中),这点与普通指针的 **浅拷贝** 不同,vptr 必须执行 **深拷贝**