很多很多年前,还在北京读研时,我去 magnetar 寻求一个实习机会时,面我的北美小哥让我解释一下C++的菱形关系,好像当时没有答上来。
今天在 OceanBase 代码里又看到了菱形关系。多重继承在我们的代码中是很少出现的,居然能再次遇到可真不容易。
问题:下面的代码里,C 的构造函数为什么必须显式调用 Base(v)?
class Base { explicit A(int &) {} }
class A : virtual public Base { }
class B : virtual public Base { }
class C : public A, public B
{
C(int v) : Base(v), A(), B() {}
}
更进一步地,为什么下面比较简单的场景里 C 依然要显式调用 Base(v)?
class Base { explicit A(int &) {} }
class A : virtual public Base { }
class C : public A
{
C(int v) : Base(v), A() {}
}
Well,有了一些工作经验后会积累一些设计的思想,能够更好地站在设计者角度思考问题。回答上面两个问题就比较容易了:
C++ 设计者引入了一条简单规则:A、B 这样使用了 virtual 继承的对象作为中间类时,没有资格去实例化基类。
还是有点绕,更简单的思维:
A、B 这样使用了 virtual 继承的对象作为中间类时,不会为A、B 分配基类对象的结构。
只要不让他们去分配对象,那么自然就需要子类去分配对象,子类分配了对象,自然就需要主动调用 Base 的构造函数。
C++设计者面对菱形问题时,解决思维如下:
然后遇到一个特殊情况: