在 C++ 中限制虚拟基 类 的对象共享
Limit object sharing of virtual base classes in C++
我了解 C++ 中的物理和虚拟继承。但是,我想知道这个模型是否可以通过任何设计模式或技巧以某种方式实现。
不应修改 classes E 和 F 以及它们的基数。假设它们来自外部库。
E 和 F 下面的所有内容都是开放的。可以引入中间帮助 classes、非成员函数、模板......所有可以实现这一点的东西:
BaseClass
/ \
/ \
A A
/ \ / \
/ \ / \
B C B D
\ / \ /
\ / \ /
E F
\ /
\ /
\ /
FinalClass
注意E和F不能共用一个A,FinalClass确实应该包含2个A。
我的问题是,创建 E 或 F 需要 B、C 和 D 虚拟继承 A。但是,如果 A 是虚拟基 class,则编译器只会在 FinalClass 中创建一个 A 对象而不是两个不同的。
所以,我认为你们中的许多人会在这里推荐组合而不是继承。但是,组合在这里建模的是 "has a" 关系,而不是 "is a" 关系。
在解释中,我希望 FinalClass 真正表现得像 E 或 F,包括能够将其转换为 classes.
这种布局在 C++ 中是不可行的,即使有额外的帮助 class。
事实上,B 继承自 A,并且您想一方面与 C 共享 A 继承权,另一方面与 D 共享 A 继承权,这要求 B、C 和 D 实际上继承自 A。但是这样一来,A 就会在钻石的两个分支中共享。
备选方案
还有哪些替代方案?
如果你设法打破钻石左右分支之间的 A 共享,你也会打破公共基础的共享。
如果你引入一些中介 classes A1, A2 来在你的分支中实现左右共享,你会被两个 B 必须继承其中之一
唯一的出路可能是为 B 复制一个 class。
最后一个解决方案不满足您的要求,但看起来如下:
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 以上。在这种情况下你的选择非常有限:
- 您公开继承
- 你通过一些中介私下继承 classes
但是共享的效果是一样的,如下代码演示(在上面的代码之上):
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)吗?
- 在两个分支之间有一个 shered 是不是不能接受(毕竟可能有正当理由)?
如果你对这两个问题的回答都是否,你将不得不进行组合,并实现一种 proxy design pattern,其中你的组合对象是代理你的两个组件。
我了解 C++ 中的物理和虚拟继承。但是,我想知道这个模型是否可以通过任何设计模式或技巧以某种方式实现。
不应修改 classes E 和 F 以及它们的基数。假设它们来自外部库。
E 和 F 下面的所有内容都是开放的。可以引入中间帮助 classes、非成员函数、模板......所有可以实现这一点的东西:
BaseClass
/ \
/ \
A A
/ \ / \
/ \ / \
B C B D
\ / \ /
\ / \ /
E F
\ /
\ /
\ /
FinalClass
注意E和F不能共用一个A,FinalClass确实应该包含2个A。 我的问题是,创建 E 或 F 需要 B、C 和 D 虚拟继承 A。但是,如果 A 是虚拟基 class,则编译器只会在 FinalClass 中创建一个 A 对象而不是两个不同的。
所以,我认为你们中的许多人会在这里推荐组合而不是继承。但是,组合在这里建模的是 "has a" 关系,而不是 "is a" 关系。 在解释中,我希望 FinalClass 真正表现得像 E 或 F,包括能够将其转换为 classes.
这种布局在 C++ 中是不可行的,即使有额外的帮助 class。
事实上,B 继承自 A,并且您想一方面与 C 共享 A 继承权,另一方面与 D 共享 A 继承权,这要求 B、C 和 D 实际上继承自 A。但是这样一来,A 就会在钻石的两个分支中共享。
备选方案
还有哪些替代方案?
如果你设法打破钻石左右分支之间的 A 共享,你也会打破公共基础的共享。
如果你引入一些中介 classes A1, A2 来在你的分支中实现左右共享,你会被两个 B 必须继承其中之一
唯一的出路可能是为 B 复制一个 class。
最后一个解决方案不满足您的要求,但看起来如下:
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 以上。在这种情况下你的选择非常有限:
- 您公开继承
- 你通过一些中介私下继承 classes
但是共享的效果是一样的,如下代码演示(在上面的代码之上):
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)吗?
- 在两个分支之间有一个 shered 是不是不能接受(毕竟可能有正当理由)?
如果你对这两个问题的回答都是否,你将不得不进行组合,并实现一种 proxy design pattern,其中你的组合对象是代理你的两个组件。