【问题标题】:Multiple inheritence, selecting which virtual function to call多重继承,选择调用哪个虚函数
【发布时间】:2012-06-28 09:16:23
【问题描述】:

我有以下代码示例:

class A
{
public:
    A(int a):AA(a) {};

    int AA;

    virtual int Test() 
    {
        return AA;
    };
};

class B
{
public:
    B(int b):BB(b) {};

    int BB;

    virtual int Test() 
    {
        return BB;
    };
};

class C:public A, public B
{
public:
    C(int a, int b, int c) :A(a),B(b),CC(c) {};

    int CC;
};

int main()
{
    A *a = new C(1,2,3);
    B *b = new C(1,2,3);
    C *c = new C(1,2,3);


    int x = a->Test() ; // this is 1
    int y = b->Test() ; // this is 2
//  int z = c->Test() ; // this does not compile

    return 0;
}

我预计对 a->Test() 和 b->Test() 的调用也会模棱两可,因为对象 a 是 C,因此继承自 A 和 B,它们都具有相同的 Test() 函数。但是,它们都调用了对应于 delcared 类型的实现,而不是对象实际的类型。

谁能解释为什么这些调用没有歧义? C++ 总是这样吗?

【问题讨论】:

    标签: c++ multiple-inheritance virtual-functions


    【解决方案1】:

    事实上,一个 C 实例既是一个完整的 A 实例又是一个完整的 B 实例(因此拥有 A 方法和 B 方法的副本)

    由于 a 是 A* ,编译器将使用 C 实例内部的 A 虚拟表副本 由于 b 是 B* ,编译器将使用 C 内部的 B 虚拟表副本 实例

    您不能使用 C*,因为编译器现在不会调用 A 或 B 的哪个 Test() 方法(因为 C 类同时包含 A::Test 和 B::Test 符号)

    如果你实现了一个 C::Test() 方法,那么它将被调用而不是 A::Test() 和 B::Test() 因为方法对于 A 和 B 都是虚拟的。

    【讨论】:

    • 所以在这种情况下编译器将始终从声明的类型调用函数(假设它没有被后代覆盖)的设计行为,即我可以依靠它总是在编译器中发生吗?
    • 参见en.wikipedia.org/wiki/…,它说多虚拟表副本是多继承的“最常见”实现。我认为如果您不使用异国情调的编译器,这可能是一种可靠的行为
    • 这似乎和我编造的例子几乎一样:en.wikipedia.org/wiki/Thunk_(programming)
    • @Stefan WP 页面没有正确解释什么是 thunk。 thunk 不是 C++ 函数或正确的 asm 函数。这是跳转到正确函数的一小段代码。它不会 POP/PUSH/RET。
    • @dweeves "它说多个虚拟表副本是多重继承的“最常见”实现。" 它不是“最常见的”,它是 现有方法。
    【解决方案2】:

    因为A不知道C的存在。

    考虑一个稍微不同的场景:

    foo.h

    class A { public: virtual void Test() {} };
    
    void myFunction(A *a);
    

    foo.cpp

    #include "foo.h"
    
    void myFunction(A *a) {
        a->Test();
    }
    

    我猜你会期望它编译?但是如果我以后独立从A继承,那会不会影响这段代码的编译呢?

    【讨论】:

    • 我知道我处于一个只见树木不见森林的阶段,但我没有看到问题所在。通常我希望你的代码能够编译。困扰我的只是 Test 是虚拟的,调用应该取决于对象的实际类型,而不是声明的类型,但在实际对象中调用是模棱两可的。显然它不是在工作,但我的大脑被阻塞了为什么!!
    • 对不起,我发帖的时候只看到了你的部分回复(被诅咒的iPhone界面)。如果您从 A 继承 a 并传入指向后代的指针(其中 test 是虚拟的),我希望它调用后代版本的 Test 但如果它也从其他地方继承 Test ,编译器如何知道要调用哪个?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-31
    • 2010-10-11
    • 2014-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多