21

我试过这段代码:

class A
{
    virtual void foo() = 0;
};

class B
{
    virtual void foo() = 0;
};

class C : public A, public B
{
    //virtual void A::foo(){}
    //virtual void B::foo(){}

    virtual void A::foo();
    virtual void B::foo();
};

void C::A::foo(){}
void C::B::foo(){}

int main()
{
    C c;
    return 0;
}

使用注释部分是可以的,但是当我尝试在类声明之外编写定义时,编译器会报告错误。我正在使用 MSVC11 编译器,有人知道如何编写吗?我需要将代码移动到 cpp 文件中。

谢谢~~

4

5 回答 5

31

函数根据名称和参数类型覆盖基类的虚函数(见下文)。因此,您的类C两个虚函数foo,一个继承自AB。但是一个函数void C::foo()会覆盖两者

[class.virtual]/2

如果在一个类和一个类vf声明了一个虚成员函数,直接或间接地派生自一个与被声明,然后也是虚拟的(无论它是否被声明)并且它覆盖.BaseDerivedBasevfBase::vfDerived::vf Base::vf

正如我在评论中已经说过的,[dcl.meaning]/1 禁止在(成员)函数的声明中使用限定 ID :

declarator-id被限定时,该声明应引用该限定符所引用的类或命名空间的先前声明的成员 [...]"

因此任何virtual void X::foo();作为里面的声明都是非法的C

编码

class C : public A, public B
{
    virtual void foo();
};

是 AFAIK 覆盖的唯一方法foo,它将同时覆盖A::fooB::foo。除了引入另一层继承之外,没有办法为不同的行为A::foo提供两种不同的覆盖:B::foo

#include <iostream>

struct A
{
    virtual void foo() = 0;
};

struct B
{
    virtual void foo() = 0;
};

struct CA : A
{
    virtual void foo() { std::cout << "A" << std::endl; }
};

struct CB : B
{
    virtual void foo() { std::cout << "B" << std::endl; }
};

struct C : CA, CB {};

int main() {
    C c;
    //c.foo();  // ambiguous

    A& a = c;
    a.foo();

    B& b = c;
    b.foo();
}
于 2013-08-23T10:48:03.790 回答
4

你只有一个虚函数foo

class A {
    virtual void foo() = 0;
};

class B {
    virtual void foo() = 0;
};

class C : public A, public B {
    virtual void foo();

};

void C::foo(){}
void C::A::foo(){}
void C::B::foo(){};

int main() {
    C c;
    return 0;
}
于 2013-08-23T09:12:10.990 回答
3

我遇到了同样的问题,不小心打开了第二个线程。对此感到抱歉。对我有用的一种方法是在没有多重继承的情况下解决它。

#include <stdio.h>

class A
{
public:
    virtual void foo(void) = 0;
};

class B
{
public:
    virtual void foo(void) = 0;
};


class C
{
    class IA: public A
    {
        virtual void foo(void)
        {
            printf("IA::foo()\r\n");
        }
    };
    class IB: public B
    {
        virtual void foo(void)
        {
            printf("IB::foo()\r\n");
        }
    };

    IA m_A;
    IB m_B;
public:
    A* GetA(void)
    {
        return(&m_A);
    }

    B* GetB(void)
    {
        return(&m_B);
    }
};

诀窍是将派生自接口(A 和 B)的类定义为本地类(IA 和 IB),而不是使用多重继承。此外,如果需要,这种方法还打开了每个接口的多个实现的选项,而这在使用多重继承时是不可能的。本地类 IA 和 IB 可以很容易地访问类 C,因此接口 IA 和 IB 的实现可以共享数据。

每个接口的访问可以如下进行:

main()
{
    C test;
    test.GetA()->foo();
    test.GetB()->foo();
}

...并且关于 foo 方法不再有歧义。

于 2018-06-18T14:18:37.083 回答
1

您可以使用不同的函数参数来解决这种歧义。

在现实世界的代码中,这样的虚函数会做一些事情,所以它们通常已经有:

  1. A 和 B 中的不同参数,或
  2. 为了解决这个继承问题,您可以将 A 和 B 中的不同返回值转换为 [out] 参数;否则
  3. 您需要添加一些标签参数,优化器会丢弃这些参数。

(在我自己的代码中,我通常会发现自己在情况(1)中,有时在(2)中,在(3)中从未如此。)

你的例子是案例(3),看起来像这样:

class A
{
public:
    struct tag_a { };
    virtual void foo(tag_a) = 0;
};

class B
{
public:
    struct tag_b { };
    virtual void foo(tag_b) = 0;
};

class C : public A, public B
{
    void foo(tag_a) override;
    void foo(tag_b) override;
};
于 2020-09-29T22:15:48.343 回答
0

对阿迪戈斯汀溶液的轻微改进:


#include <iostream>

struct A {
    virtual void foo() = 0;
};

struct B {
    virtual void foo() = 0;
};

template <class T> struct Tagger : T {
    struct tag {};
    void foo() final { foo({}); }
    virtual void foo(tag) = 0;
};

using A2 = Tagger<A>;
using B2 = Tagger<B>;

struct C : public A2, public B2 {
    void foo(A2::tag) override { std::cout << "A" << std::endl; }
    void foo(B2::tag) override { std::cout << "B" << std::endl; }
};

int main() {
    C c;
    A* pa = &c;
    B* pb = &c;
    pa->foo(); // A
    pb->foo(); // B
    return 0;
}

假设基类AB是给定的并且不能被修改。

于 2022-01-06T06:30:08.360 回答