2

我正在尝试使用多重继承来解决我正在开发的复杂层次结构。情况如下:

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不知道BC只是A. 这是我想保留的要求。

编辑:即使两者都是纯虚拟的,同样的答案foo()是否适用AB(例如= 0

4

5 回答 5

3

D将有两个。它们可以在类中A :: foo()B :: foo()类内访问。

于 2012-09-13T17:00:45.943 回答
2

实际上, 的实现将同时D::foo覆盖两者。A::fooB::foo

当然,这两个继承的函数仍然可用,可以用它们的全名来调用。但这与单继承情况没有什么不同。

关于ClientClass,你创建一个D对象,然后foo通过一个指针调用A. 因此D::foo将调用覆盖。

如果您想要不同版本的andfoo覆盖(例如,因为它们不相关但碰巧以相同的方式调用),那么您将需要做一些工作:A::fooC::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!
}
于 2012-09-13T17:08:46.377 回答
2

该类D具有三个功能fooD::fooB::fooA::foo

由于在和foo中都是虚函数,因此将不合格的函数调用动态分派给最派生的函数:AB

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();
}
于 2012-09-13T17:33:25.063 回答
1

您遇到了多重继承的经典问题,可能是最重要的一个建议非常有限地使用它(通过混合或接口/虚拟类的概念)。

请查看SO的这篇文章,以获得更全面的解释和您拥有的各种替代方案。

于 2012-09-13T17:02:00.937 回答
0

在这段代码中:

class ClientClass {
  void method() {
    A *a = new D();
    a->foo();
  }

D 的 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 函数,你会得到一个编译错误。

于 2012-09-13T17:30:26.680 回答