Point3d(float x = 0.0, float y = 0.0, float z = 0.0)
: Point(x, y), _z(z) { }
Point3d(const Point3d& rhs)
: Point(rhs), _z(rhs._z) { }
~Point3d();
Point3d& operator=(const Point3d&);
virtual float z() { return _z; }
private:
float _z;
}
复制代码
传统的constructor扩张并没有用,因为virtual base class的共享性的缘故原由。试想以下继承关系
class Vertex : virtual public Point { };
class Vertex3d : public Point3d, public Vertex { };
class PVertex : public Vertex3d { };
复制代码
当point3d和vertex同为vertex3d的subobject时,对point constructor的调用操作一定不可以发生,作为一个最底层的class pvertex,有责任将point初始化
constructor的函数本体因而必须条件式地侧测试传进来的参数,然后决定调用或不调用相关的virtual base class constructor。
Point3d的constructor的扩充如下
Vertext3d 的constructor 正确的调用 Pointer constructor。 virtual base class constructor被调用有着明白的界说,只有当一个完整的class object被界说出来(比方 Point3d origin)时,它才会被调用,如果object只是某个完整的object的subobject,他就不会被调用
还可以把constructor一分为2,一个针对一个完整的object,一个针对subobject,完整的object无条件调用virtual base constructor,设定vptrs,subobject不调用virtual base constructor,也可能不设定vptrs
二、Vptr初始化语义学
重点:Vptr在那一步做的
界说一个PVertex object时,constructor的调用次序是
Point(x, y);
Point3d(x, y, z);
Vertex(x, y, z);
Vertex3d(x, y, z);
PVertex(x, y, z);
复制代码
假设每个class都界说了一个virtual function size();返回该class的大小。我们来看看界说的PVertex constructor
PVertex::Pvertex(float x, float y, float z)
: _next(0), Vertex3d(x, y, z), Point(x, y) {
if(spyOn)
cerr << "Within Pvertex::PVertex()"
<< "size: " << size() << endl;
}
复制代码
编译器怎样包管恰当的size()被正确的调用? 如果调用操作必须在 constructor 或 destructor 中, 那么答案非常显着:将每一个调用操作以静态决议,千万不要用虚拟机制
vptr的初始化操作:在base class constructor调用操作之后,但是在步伐员供应的代码或”member initialization list中全部列的members初始化操作”之前。如果每一个constructor 都不停等候其 base class construtors 执行完毕之后才设定其对象的vptr,那么每次都可以或许调用正确的 virtual function 实例。
constructor的执行算法通常如下:
在derived class constructor中,全部的virtual base class以及上一层的base class的constructor会被调用。
上述完成之后,对象的vptrs被初始化,指向相关的虚表。
如果有member initialization list的话,将在constructor的函数体内展开,这必须是在vptr设定之后才做的,以免有一个virtual member function被调用。
在class的 constructor 的 member initialization list中调用该class的一个虚函数,这是安全的。因为vptr的设定总包管在member initialization list扩展之前就被设定好;但是这在语义上时不安全的,因为函数本身可能依赖未被设定初值的成员。
当需要为base class constructor提供参数时,在class的constructor的成员初值列中调用该class的一个虚函数这就是不安全的,此时vptr要么尚未被设定好,要么指向错误的class。该函数存取任何class's data members一定还没有初始化。(子类初始化列表->初始化父类,子类初始化列表含有虚函数的情况)