4

我有这种代码:

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

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

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

或使用虚拟继承:

class A: public virtual Base

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

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

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

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

4

3 回答 3

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
};

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

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

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

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

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

于 2013-02-19T14:34:50.103 回答
0

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

class A: public Base
{
  public:
     A() : Base(refObjectForA) {}
     virtual ~A() {}
  protected:
    using Base::Base;
};
于 2022-01-11T10:53:45.620 回答