三个重要函数:构造函数,析构函数,拷贝构造函数。
1. 无继承情况下的对象构造。
当类中存在虚函数时,编译器会对该类产生膨胀作用, 例如如下类:
- class Point {
- public:
- Point(float x = 0.0, float y = 0.0)
- : _x(x), _y(y){}
- virtual float z();
- protected:
- float _x, _y;
- };
a. 我们所定义的构造函数中,会被附加一些代码,一边初始化虚表指针(vptr),这些代码会安插在任何基类构造函数的调用之后,但必须在任何使用者供应的代码之前,一种可能的上述Point类的构造函数扩展:
- Point* Point::Point (Point* this, float x, float y)
- : _x(x), _y(y)
- {
- //设定虚表指针
- this->__vptr_Point = __vtbl__Point;
- //扩展成员变量初始化列表
- this->_x = x;
- this->_y = y;
- //传回this对象
- return this;
- }
b. 合成一个拷贝构造和拷贝赋值函数,这两个函数是重要的,会被调用的,用来在这两个函数被调用时,设定虚表指针等操作,可能的扩展结果:
- //copy constructor
- inline Point* Point::Point (Point* this, const Point& rhs)
- {
- //设定虚表指针
- this->__vptr_Point = __vtbl__Point;
- //数据成员拷贝
- //传回this对象
- return this;
- }
2. 继承情况下的对象构造。
当我们定义一个类对象时:T object,构造函数可能会发生扩展:
a. 初始化列表中数据成员初始化操作会放入构造函数本身,注意是以数据成员在类中的声明顺序,而非在初始化类表中的顺序。
b. 如果有某一个数据成员未出现在初始化列表中,但是他有默认构造函数,则该默认构造函数会被调用。
c. 在那之前,如果如果类该对象有虚表指针,它(们)会设定初值,指向适当的虚表(们)。
d. 在那之前,所有基类构造函数会被调用,按照它们在子类中的继承顺序:
i. 如果基类在初始化列表中,任何明确指定的参数都需要传递进去。
ii. 如果基类没有列于初始化列表,那么会调用可能存在的默认构造函数。
iii. 如果基类是多重继承下的第二个或者后继,那么此时子类的this指针需要被调整,以指向该基类部分(subobject)。
e. 在那之前,所有虚基类的构造函数必须被调用, 从左到右,从最深到最浅。
i. 如果基类在初始化列表中,任何明确指定的参数都需要传递进去,如果没有,会调用可能存在的默认构造函数。
ii. 类中每一个虚基类子对象的偏移(offset)必须在执行期可被存取。
iii. 如果类对象是最底层的(most-derived),其构造函数可能会被调用,某些支持这个行为的机制会被放入(下文提到的__most_derived)。
继续以Point为例,进行一下扩充,并声明Line类:
第二个构造函数会被扩展为:
析构函数会被扩展为:
虚拟继承情况下,子类构造函数的扩展有些不一样,考虑如下类以及其继承体系:
由于虚基类在子类中只有一份,所以虚基类Point的构造函数只能被调用一次,例如,在实例化Point3d或者Vertex对象时,作为二者直接基类的Point的构造函数需要调用,但是在定义Vertex3d对象时,会调用Point3d和Vertex的构造函数,此时二者的构造不应该调用Point的构造函数,因为Point已经在Vertex3d构造函数中调用过了,为了保证虚基类的唯一性,不允许再调用,也就是只有最下层的子类构造函数中需要调用虚基类的构造函数,Point3d的构造函数以及Vertex3d的构造函数的可能扩展如下,注意红框中的内容:
在Vertex3d构造函数中,当调用其父类Point3d以及Vertex的构造函数时,__most_derived参数会设定为false,已压制而这对于Point的再次初始化:
3. 关于虚表指针的初始化在子类构造函数中的情况。
a. 子类构造函数中,所有虚基类以及基类的构造函数会被调用。
b. 接着是对象的虚表指针被初始化,指向相关的虚表。
c. 在构造函数内展开可能存在的数据成员初始化列表,由于此时可能有虚函数调用,所以该步必须在虚表指针初始化妥当之后。
d. 最后,指向我们码农提供的代码。
例如,之前例子中的PVertex类的某个构造函数如下:
可能会被扩展成为,下图貌似有一点遗漏,this->Vertex3d::Vertex3d(x, y, z)应该是:this->Vertex3d::Vertex3d(false, x, y, z)以防止虚基类多次构造。