虚拟但不是多重继承来调用祖父构造函数

Virtual but not multiple inheritance to call grandparent constructor

本文关键字:调用 构造函数 多重继承 虚拟      更新时间:2023-10-16

我有这样的代码:

class Ref {<undefined>};
Ref refObjectForA, refObjectForB;
class Base
{
  public:
    Base(const Ref & iRef) : _ref(iRef) {}
    virtual ~Base() {}
    const Ref & ref;
};
class A: public Base
{
  public:
     A() : Base(refObjectForA) {}
     virtual ~A() {}
};
class B: public A
{
  public:
    B() : Base(refObjectForB) {} // won't compile: Base is not direct base of B
    virtual ~B() {}
};

由于该属性是引用,我认为我只能在构造函数中设置它,因此我需要在B()中调用Base构造函数。 我找到了两种方法:在A中提供一个"前向"构造函数(但这意味着在所有可能继承的类中添加代码(:

A(const Ref& iRef): Base(iRef)

或使用虚拟继承:

class A: public virtual Base

第二个选项允许在实现中使用更直接的代码B但我想知道我是否在一个丑陋的技巧中滥用虚拟继承,或者它是否是一个有效的用例。

  • 在这种情况下,我可以使用虚拟继承吗?
  • 如果不是,出于什么原因?

我发现的"意外"行为之一是,由于虚拟继承,无法将Base指针static_castB指针。

此外,我也想知道它为什么有效(我的意思是为什么B().ref == refObjectForB(:我认为在 B() 中对默认A()构造函数的隐式调用会在显式Base构造函数之后覆盖 ref 属性,但虚拟继承可能并非如此。

如果您想

坚持继承层次结构,我可以看到的最佳选择是实现受保护的构造函数,这些构造函数将转发给Base类。 保护构造函数可确保不能使用此构造函数构造(最终(实例,因此它只会在子类中用于初始化超类。

使用一些或多或少丑陋和危险的宏,这变得很容易编写:

#define REF_FORWARD_CTOR(ClassName, DirectSuperClassName) 
    protected: ClassName(class Ref &r) : DirectSuperClassName(r) {} 
    public:
class A : public Base
{
    REF_FORWARD_CTOR(A, Base)
public:
    A() : Base(refObjectForA) {} // normal ctor
};
class B : public A
{
    REF_FORWARD_CTOR(B, A)
public:
    B() : A(refObjectForB) {} // normal ctor
};

另一种设计是让AB都(直接(从Base派生。然后,使用多重继承和"公共类"(可能是私有的(添加功能,具体取决于它们的用途:

class Base {
};
class Common {
    // common stuff used by both A and B
};
class A : public Base, public Common {
    // no further stuff here
};
class B : public Base, public Common {
    // add more stuff, or put it in a common super-class again,
    // if some classes want to inherit from B again
};

这种设计的问题在于Common中的功能无法访问AB中的内容。要解决此问题,请执行以下操作之一:

  • 如果只需要静态的东西:使用 CRTP 在具体的Common类型中指定A/B:然后Common<A>可以使用 A::... ,但与A的具体实例没有任何关系
  • 如果需要实例:在Common的构造函数中提供指针/引用(略有开销(
  • 将前两个解决方案放在一起:使用 CRTP,在 A 中实现包装器函数,并在B中调用Common<A>函数,Common<B>通过额外参数提供this(这是一个A*B*(。
  • 与上面相同,但是如果您在此指针参数上重载/模板化这些函数("函数上的 CRTP",如果您想这样称呼它(,类Common也可以是非模板化的(没有 CRTP(。代码胜于雄辩。(示例代码没有您的引用,专注于"公共类"。
是的,

从技术上讲,您可以使用虚拟继承来实现在最派生的类中提供引用的目标。

是的,这是一种设计气味。

您的类除了它们的直接基础之外,不需要知道任何东西(当出于其他原因需要虚拟继承时,虚拟继承是规则的例外(。

坚持提议的类层次结构,为了解决问题,使用"using-声明"将 Base 的构造函数带到类 A 的受保护部分就足够了,即:

class A: public Base
{
  public:
     A() : Base(refObjectForA) {}
     virtual ~A() {}
  protected:
    using Base::Base;
};