我尝试使用多重继承来解决我正在开发的复杂层次结构。情况如下:
class A {
virtual void foo();
}
class B {
virtual void foo();
}
class C : public B {
}
class D : public A, public C {
void foo() { ... }
}
class ClientClass {
void method() {
A *a = new D();
a->foo();
}
我想知道的是:D
最终会只有一个函数foo()
吗?我正在思考这个问题,因为这个方法在父母双方都是虚拟的,所以他们应该在同一个方法上准直,但我只是因为我来自Java并且觉得它在C ++中可能有所不同。我必须两次声明虚函数foo()
,因为ClientClass
不知道B
或C
,只知道A
。这是我想要保留的要求。
编辑:即使foo()
和A
中的B
都是纯虚拟,也会应用相同的答案吗? (例如= 0
)
答案 0 :(得分:3)
D将有两个。可以使用课程内的A :: foo()
和B :: foo()
访问它们。
答案 1 :(得分:2)
实际上,D::foo
的实施会同时覆盖A::foo
和B::foo
。
当然,两个继承的函数仍然可以通过它们的全名来调用。但这与单一继承案例没有什么不同。
关于ClientClass
,您创建了一个D
对象,然后通过指向foo
的指针调用A
。因此将调用D::foo
覆盖。
如果您想要覆盖foo
和A::foo
的不同版本的C::foo
(例如,因为它们不相关但恰好以相同的方式调用),那么您将需要一点工作:
class A2 : public A
{
public:
virtual void foo()
{
A_foo();
}
virtual void A_foo()
{
A::foo();
}
};
class C2 : public C
{
public:
virtual void foo()
{
C_foo();
}
virtual void C_foo()
{
C::foo();
}
};
class D: public A2, C2
{
public:
virtual void A_foo()
{ /* ... */ }
virtual void C_foo()
{ /* ... */ }
};
现在使用:
{
A *a = new D;
a->foo(); //will call A2::foo -> D::A_foo
B *b = new D;
b->foo(); //will call C2::foo -> D::C_foo
D *d = new D;
d->foo(); //error: ambiguous call!
}
答案 2 :(得分:2)
课程D
有三个功能foo
:D::foo
,B::foo
和A::foo
。
由于foo
和A
中的B
都是虚拟的,因此会将非限定函数调用动态调度到最派生的函数:
void f(A & a) { a.foo(); }
void g(B & b) { b.foo(); }
int main()
{
D d;
f(d); // calls d.D::foo();
g(d); // calls d.D::foo();
d.foo(); // calls d.D::foo();
d.B::foo(); // calls d.B::foo();
d.A::foo(); // calls d.A::foo();
}
答案 3 :(得分:1)
你已经遇到了多重继承的经典问题,可能是最重要的一个,建议使用它非常有限(通过mixins或接口/虚拟类的概念)。
请查看this post from S.O以获得更全面的解释和各种替代方案。
答案 4 :(得分:0)
在此代码中:
class ClientClass {
void method() {
A *a = new D();
a->foo();
}
D's foo会跑。你仍然可以使用a-> A :: foo()访问A foo。 请注意,您初始化'a'的方式无法达到B foo()。 但是每次你构建一个D实例时它将使用D foo,因为两个foo函数都被声明为虚拟。
对于代码中的每个类,您都有一个函数表。
每个对象的表由类型定义。这意味着A * a = new D()
具有A表功能。
如果一个函数在它之前有一个虚拟标志,这意味着如果构建一个派生类的实例,它的表将包含一个指向派生类函数的指针。
这意味着在ClinetClass的代码中,'a'对象具有A函数表,其中foo函数包含指向D foo函数的指针。
请注意,如果D没有自己的foo,你会写
D * d = new D();
d-> foo();
如果他不知道应该使用哪种foo函数,你会得到一个编译错误。