无需虚拟继承即可解决钻石问题

问题描述 投票:0回答:2

情况是这样的:

class A
{
public:
  A();
  void actionA();
private:
  int a;
};

class B1
  : public A
{
  ...
};

class B2
  : public A
{
  ...
};

class OrthogonalFeature
  : public A
{
  void orthogonalFeature();
};

class C1
  : public B1
  , public OrthogonalFeature
{
    ...
};

class C2
  : public B2
  , public OrthogonalFeature
{
    ...
};

class User
{
public:
  void run() {
    c.actionA();
    c.orthogonalFeature();
  }
private:
  MyNeededType c; // c is either C1 or C2
};

User
类需要拥有一个可以执行
actionA()
actionB()
的对象。

因此,我可能想创建类

MyNeededType
,它需要继承于
A
和(
B1
B2
),并且我遇到了钻石问题(数据成员
a
不明确)。解决这个问题的常用方法是使用虚拟继承。但是,对于这个项目,出于性能原因,我不能使用虚拟继承(请仅接受表面值)。

所以我有一个问题。可能有帮助的是

User
类只能在我拥有的 2 个现有类上运行:
C1
C2
。所以我想到的另一个选择是让
C1
C2
继承
A
,并在这些派生类中重新实现
orthogonalFeature
,而不是继承
OrthogonalFeature
。因此没有钻石。看起来是这样的:

class C1
  : public B1
{
   public:
     void orthogonalFeature();
};

class C2
  : public B2
{
    void orthogonalFeature();
};

我喜欢这个解决方案,但是如何在

c
中指定
User
的类型?我觉得我需要创建一个类型,这意味着:这个类是
C1
C2
。但我不知道c++中存在这个?

欢迎任何想法,谢谢!

c++ c++14 diamond-problem
2个回答
0
投票

快速访问 A 数据成员对您来说有多重要?最终,虚拟继承使您可以轻松访问 A::a,但要付出一定的代价才能访问 B2 和 B1 中的几乎所有其他内容。

不使用钻石,分享A::a能达到你想要的效果吗?在COM模型中,所有接口类都使用从IUnknown接口继承的简单继承,但是实例化类必须实现最终的“真正的基类”函数才能获得“注册的”IUnknown。当然,该模型中没有数据成员。

您可以简单地设置

B1: public trueA
B2: public indirectA
,然后在您的实现中,有一个模板包装器“激活”indirectA 以找到 trueA。实际上,您还可以让您的实现包装器从 trueA、B1、B2 派生,并将indirectA 的 B1 和 B2 实例修复到它吗?

但请注意,这依赖于 B1 和 B2 的构造函数不引用 A,因为它尚未初始化。

可以肮脏地破解indirectA的构造函数以“知道”它实际上是在C:B2中实例化的,并推断出C:B1:A或C:A,并且您可以通过使用模板来推动定义来获得相当干净的结果,也许还有一点 CRTP?


0
投票

我想说更喜欢通过设计组合而不是多重继承来解决钻石问题。

© www.soinside.com 2019 - 2024. All rights reserved.