【问题标题】:Limit object sharing of virtual base classes in C++限制 C++ 中虚拟基类的对象共享
【发布时间】:2015-07-04 00:18:55
【问题描述】:

我知道 C++ 中的物理和虚拟继承。但是,我想知道这个模型是否可以通过任何设计模式或技巧来实现。

不应修改 E 和 F 类及其基类。想象它们来自外部库。

E 和 F 以下的所有内容都是开放的。可以引入中间帮助类、非成员函数、模板......所有可以实现这一点的东西:

           BaseClass
           /       \
          /         \
         A           A
        / \         / \
       /   \       /   \
      B     C     B     D
       \   /       \   /
        \ /         \ /
         E           F
          \         /
           \       /
            \     /
           FinalClass

注意,E 和 F 不能共享一个 A。FinalClass 确实应该包含 2 个 A。 我的问题是,创建 E 或 F 需要 B、C 和 D 虚拟继承 A .. 但是,如果 A 是虚拟基类,那么编译器只会在 FinalClass 中创建一个 A 对象,而不是两个不同的那些。

所以,我想你们中的许多人会在这里推荐组合而不是继承。但是,组合在这里建模“具有”关系而不是“是”关系。 在解释中,我希望 FinalClass 真正表现得像 E 或 F,包括能够将其转换为这些类。

【问题讨论】:

  • 你能把A做成类模板吗?
  • 假设我可以。它会改变什么? B 类还必须决定要继承 A 的哪个特化。
  • 如果你可以改变你的类名(模板只是一种改变类名的方法),你可以解决你的约束template <class T> class B<T> : public virtual A<T> {

标签: c++ inheritance multiple-inheritance modeling virtual-inheritance


【解决方案1】:

这种布局在 C++ 中是不可行的,即使有额外的帮助类。

B 继承自 A 的事实,并且您希望一方面与 C 共享 A 继承,另一方面与 D 共享继承,这要求 B、C 和 D 虚拟继承自 A。但随后将共享 A穿过钻石的两个分支。

替代方案

还有什么选择?

  • 如果你设法打破钻石左右分支之间的 A 共享,你也会打破公共基础的共享。

  • 如果您要引入一些中间类 A1、A2 来在您的分支中实现左右共享,那么您会遇到这样一个事实,即 B 都必须继承一个或另一个

  • 唯一的出路可能是为 B 提供一个重复的类。

最后一个解决方案不满足您的要求,但如下所示:

struct Base { int x; };
struct A : public virtual Base { int a; };
struct AC : public A{};  // synonym 
struct B : public virtual A { int b; };
struct BC : public virtual AC { int b;  }; // !! clone !! 
struct C : public virtual AC { int c; };
struct D : public virtual A { int d; };
struct E : public BC, C { int e; };
struct F : public B, D { int f; };
struct Final : public E, F { };

这里是会员的访问权限:

Final f;
f.x = 2;    // unambiguous:  there's onely one of it 
f.f = 1; 
f.e = 2; 
f.d = 3; 
f.c = 4; 
//f.b = 5;   // ambiguous:  there are two of it 
f.E::b = 5;  // successful desambiguation
f.F::b = 6;  // successfuldesambiguation
//f.a = 7;   // ambiguous:  there are two of it
f.E::a = 7;  // successful desambiguation
f.F::a = 8;  // successful desambiguation

回到你的问题陈述:你说你不能在 E 和 F 以上进行干预。在这种情况下,你的选择非常有限:

  • 你公开继承
  • 您通过一些中间类私下继承

但是共享的效果是一样的,如下代码演示(在上面的顶部):

class FI : private F  // make F private
{ public: 
    void set_xa(int u, int v) { x = u; a= v; }
    void show_xa() { cout << "x:" << x << " a:" << a << endl; }
};
class EI : private E  // make E private
{
public:
    void set_xa(int u, int v) { x = u; a = v; }
    void show_xa() { cout << "x:" << x << " a:" << a << endl; }
};

struct Final3 : public EI, public FI { }; 

Final3 h;
h.EI::set_xa(3, 4);
h.FI::set_xa(5, 6);
h.EI::show_xa(); 
h.FI::show_xa();
// the shared virtually inherited memebers are still shared !

结论:

通过继承,您完全受制于 E 和 F 之上的设计,不允许您影响。

所以第一个问题是:

  • 难道你不能改变这个设计(即克隆一个 B)吗?
  • 在两个分支之间放置 A 不是不可接受的吗(毕竟可能有正当理由)?

如果您对这两个问题的回答都是否定的,那么您将不得不进行组合,并实现一种 proxy design pattern,其中您的组合对象是您的两个组件的代理。

【讨论】:

  • 如果A不是虚拟的,B和C或B和D如何共享一个A?
  • @JackSabbath - 完全正确,抱歉:我已经快速阅读了这个问题,并没有注意到两个常见的 B。我已经进行了全面的编辑。
  • 非常感谢您的详细解释。实际上我问过这个问题,因为我是一个库的作者,所以我想在实现它之前弄清楚这种设计模式是否会是死胡同。我认为我的模型可以实现,如果 E 在物理上从 A 派生,而 B 和 C 不会从 A 派生,而是使用奇怪的循环模板模式来访问 E 继承的 A。图表的另一侧也是如此。当然,B 不会有相同的类型(CRTP 专业化)。他们需要一个接口基类。我最好永远忘记这种模式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-19
  • 1970-01-01
  • 2014-05-24
  • 2016-02-10
  • 2014-08-16
  • 2019-08-11
相关资源
最近更新 更多