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

时间:2013-02-19 14:26:26

标签: c++ virtual-inheritance

我有这样的代码:

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() {}
};

由于属性是引用,我想我只能在构造函数中设置它,所以我需要在Base中调用B()构造函数。  我发现了两种方法:在A中提供“前向”构造函数(但这意味着在可能继承的所有类中添加代码):

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

或使用虚拟继承:

class A: public virtual Base

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

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

我发现的一个“意外”行为是,由于虚拟继承,static_cast指向Base指针的B指针是不可能的。

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

2 个答案:

答案 0 :(得分:4)

如果你想坚持你的继承层次结构,我可以看到的最佳选择是实现受保护的构造函数,它们将引用它们转发到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在具体的A类型中指定B / CommonCommon<A>然后可以使用{{1但是,与A::...
  • 的具体实例没有任何关系
  • 如果需要实例:在A的构造函数中提供指针/引用(略微开销)
  • 将前两个解决方案放在一起:使用CRTP,在CommonA中实现包装函数,这些函数调用BCommon<A>中提供Common<B>的函数(这是thisA*通过额外参数。
  • 与上面相同,但是如果你在这个指针参数上重载/模板这些函数,那么类B*也可以是非模板化的(没有CRTP)(“CRTP on functions”,如果你想调用它就像那)。 Code speaks louder than words.(示例代码没有您的引用,并侧重于“公共类”。)

答案 1 :(得分:2)

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

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

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