我遇到如下情况:
class A {
virtual void f() { /*some default impl here*/}
virtual void g() { /*some default impl here*/}
};
class B : public A {
virtual void f() { /* do some specific stuff and call parent's f()*/}
virtual void g() { /* do some specific stuff and call parent's g()*/}
};
class C : public A {
virtual void f() { /* do some specific stuff and call parent's f()*/}
virtual void g() { /* do some specific stuff and call parent's g()*/}
};
class mixed /* I don't know how to derive*/ {
virtual void f() { /* call B::f()*/}
virtual void g() { /* call C::g()*/}
};
我在这里考虑多重继承。即,使mixed
来自B
和C
。
但是有一些已知的问题(例如,
Diamond problem)。
另一种解决方案可能是构图。
但是什么是正确的解决方案,请告知:)
提前致谢!
答案 0 :(得分:4)
一些已知问题(例如,钻石问题)。
首先:除非您明确创建菱形图案,否则没有菱形图案。
class mixed: public B, public C
这将使B和C混合继承。每个都有自己的显式A(无钻石)。
由于B和C都有从A派生的虚拟成员,因此应该调用哪一个是不明确的,但是你已经通过在mixed
中明确定义所有虚拟来解决这个问题(所以问题已解决)。
void mixed::f() { B::f();} // works fine.
现在即使您明确创建钻石。
注意:菱形图案不会正常显示。菱形图案是您必须显式制作的设计决策,您可以使用它来解决某些类型的问题(通过使用虚拟继承)。
class B: public virtual A ...
class C: public virtual A ...
class mixed: public B, public C ...
你仍然没有问题。因为mixed::f()
仅使用B
分支(然后是A)。虽然mixed::g()
仅使用C
分支(然后是A)。
即使A
有自己的状态(虽然这可能是一个坏主意,通常最好将接口用作虚拟基类),但我们不会遇到问题,因为mixed::f()
并且mixed::g()
只在一个孩子中调用一个函数(如果他们调用双方并且A
的状态被两个调用都变异,则问题就开始发生。
另一种解决方案可能是构图。
那也行。
class mixed: public A
{
B b;
C c;
public:
virtual void f() override {b.f();}
virtual void g() override {c.g();}
....
};
但是什么是正确的解决方案
没有正确的解决方案。这很大程度上取决于你没有提到的细节(比如A的细节)。
但是一般的建议是更喜欢构图而不是继承,但这只是一般性的建议细节总是归结为正在解决的实际问题。
答案 1 :(得分:3)
每个方法在调用父项之前必须“执行操作”这一事实会导致问题。
一种解决方案是将IFS=$'\n'
和A
作为B
类中的成员。然后,您可以在mixed
和mixed::f()
如果需要,您可以使用纯虚拟函数mixed::g()
和base
创建基类f()
。 g()
可以继承,mixed
,A
和B
也可以。当你模仿组合时,你可以提到这种可能性。
答案 2 :(得分:3)
我想你可能正在寻找这样的东西。 (对不起,这是一大堆代码,但它确实很直接。)
#include <iostream>
struct A
{
virtual void
f()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
virtual void
g()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
// Don't forget the virtual destructor.
virtual ~A() noexcept = default;
};
struct B : virtual A
{
virtual void
f() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
A::f();
}
virtual void
g() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
A::g();
}
};
struct C : virtual A
{
virtual void
f() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
A::f();
}
virtual void
g() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
A::g();
}
};
struct D : virtual B, virtual C
{
virtual void
f() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
B::f();
}
virtual void
g() override
{
std::cout << __PRETTY_FUNCTION__ << '\n';
C::g();
}
};
int
main()
{
D d {};
d.f();
std::cout << '\n';
d.g();
}
this是一个C ++ 11功能,让编译器检查你实际上是否覆盖。使用它是很好的做法,但如果您的编译器不支持它则不需要。 override
是一个GCC扩展,用于获取命名当前函数签名的字符串文字。标准C ++有__func__
,但在这里不太有用。如果编译器没有与__PRETTY_FUNCTION__
相当的功能,您可以自己键入字符串。
输出:
virtual void D::f()
virtual void B::f()
virtual void A::f()
virtual void D::g()
virtual void C::g()
virtual void A::g()
它有效,但我不认为这个漂亮的代码。组合可能是更好的解决方案。
答案 3 :(得分:1)
以下是虚拟继承的替代方法:使用CRTP将B
和C
的功能混合到M
中,同时共享公共A
,没有 vtable的开销。
#include <iostream>
struct A
{
int a;
};
template <typename T>
struct B
{
A *get_base_a() {return static_cast<T*>(this);}
void print_a() {std::cout << get_base_a()->a << '\n';}
};
template <typename T>
struct C
{
A *get_base_a() {return static_cast<T*>(this);}
void print_a() {std::cout << get_base_a()->a << '\n';}
};
struct M : A, B<M>, C<M>
{
};
int main()
{
M m;
m.a = 1;
m.B::print_a();
m.C::print_a();
return 0;
}
但请注意,您无法将M*
传递给期望B*
或C*
的函数。