虚拟继承 (Virtual Inheritance)
考虑下面这个虚拟继承:
class Point3d: public virtual Point { Point3d(float x = 0.0, float y = 0.0, flloat z = 0.0) :Point(x, y). _z(z){} Pooint(const Point3d &rhs) :Point(rhs), _z(rhs._z){} Point3d& operator=(const Point3d&); ~Point3d(); virtual float Z() {return _z;} protected: float _z; };
传统的 constructor 扩充现象并没有用, 因为 virtual base class 的共享性之故:
//不合法的 constructor 扩充内容 Point* Point::Point(Point3d *this, float x, float y, float z) { //个人管理个人的构造与析构便笺 this->Point::Point(x, y); this->__vptr_Point3d = __vtbl_Point3d; this->__vptr_Point3d__Point = __vptr_Point3d__Point; this->_z = rhs._z; return this; }
能发现上面的 Point3d constructor 扩充内容有什么错误吗?
这个, 我的理解是菱形继承中, subobject 并不负责调用最底层 class 的 constructor, 负责调用的 class 应该是菱形底部那个近亲结婚的 class, 因此扩充码之中不该有对 Point constructor 的调用. 我也希望与大家交流下你对这个扩充码的看法. 我的看法具体理由如下文:
考虑以下三种派生情况:
class Vertex: virtual public Point{...}; class Vertex3d: public Point3d, public Vertex{...}; class PVertex: public Vertex3d{...};
Vertex 的 constructor 必须也调用 Point 的constructor. 然而 当 Point3d 和 Vertex 同为 Vertex3d 的 subobjects 时, 它们对 Point constructor 的调用操作不可以发生, 取而代之的是一个最底层的 class, Vertex3d 有责任将 Point 初始化, 而后的继承则由 PVertex来完成被共享的 Point subobject 的构造.
现在暂时不要将 virtual base class 初始化, 因为这种导致constructor 中有更多的扩充的内容, 用来指示 virtual base class constructor 该不该调用. constructor 的函数本身因而必须条件式地测试传进来的参数, 然后决定调不调用相关的 virtual base class constructors. 下面就是 Point3d 的 constructor 扩充内容:
Point3d* Point3d::Point3d(Point3d *this, bool __most_derived, float x, float y, float z) { if(__most_derived != flase){ this->Point::Point(x, y); } this-> __vptr_Point3d = __vtbl_Point3d; this->__vptr_Point3d__Point = ___vtbl_Point3d__Point; this->_z = rhs._z; return this; }
在更深层的情况下, 例如 Vertex3d 调用 Point3d 和 Vertex 的 constructor 时, 总是会把 __most_derived 参数设为 false, 于是就压制了两个 constructors 中对 Point constructor 的调用操作.
//在 virtual base class 情况下的 constructor 扩充内容 Vertex3d* Vertex3d::Vertex3d(Vertex3d *this, bool__most_derived , float x, float y, float z) { if(__most_derived != false){ this->Point::Point(x, y); } //不是最底层的 class, 就只能各取所需 this->Point3d::Point3d(false, x, y, z); this->Vertex::Vertex(false, x, y); //设定 vptrs //安插 user code return this; }
采用这样的策略得以保证语意的正确无误, 当我们定义 Point3d origin; 时, Point3d constructor 可以正确的调用其 Point virtual base class subobject, 而当我们定义 Vertex3d cv 时, Vertex3d constructor 正确地调用 Point constructor. Point3d 和 Vertex 的 constructors 会做除了对 Point 的调用操作之外该做的事.
许多人可能已经注意到某种状态, 在这种状态中, virtual base class constructors 的被调用有着明确的定义: 只有当一个完整的 class object 被定义出来时, 它才会被调用; 如果 object 只是某个完整的 object 的 subobject, 它就不会被调用.
以这一点为杠杆, 我们可以产生更有效率的 constructors. 某些编译器把每个 constructor 分裂为二, 一个针对完整的 object, 另一个针对 subobject. 完整的 object 版无条件的调用 virtual base constructors, 设定所有的 vptrs 等等; subobject 版则不调用 virtual base constructors 和 vptrs. 我想还是在下一篇博客中讨论 vptrs 的设定. constructor 的分裂可以带来速度的提升, 但你用的编译器是否支持这个特性还需要自己判断.