0

我试过运行以下命令:

struct B;
struct C;
struct A{
    A() { f(this);}
    virtual A* f(A* a) {
        cout << " A::f(A)" <<endl;
        return a;
    }
    void h() { cout << typeid(*this).name() << endl;}
    void g(B* b);
};

struct B:A{ 
    B() { f(this); }
    virtual A* f(A* a) {
        cout << "B::f(A)" << endl;
        return a;
    }

    virtual B* f(B* b) {
        cout << "B::f(B)" << endl;
        return b;
    }

    virtual C* f(C* c) {
        cout << "B::f(C)" << endl;
        return c;
    }
};

struct C: B{};

void A::g(B* b) { cout << typeid(*this).name() << endl; f(b);};

int main(){
    B* b = new B();
    cout << "------" << endl;
    C* c = new C();
    cout << "------" << endl;
    c->g(b);
    return 0;
}

请注意, g() 是非虚拟的,因此它是在编译期间选择的。

运行它时,我得到以下输出:

A::f(A)
B::f(B)
------
A::f(A)
B::f(B)
------
1C
B::f(A) <----- Notice this

请注意,最后一行似乎调用了 f(),就好像它是动态绑定的,但只调用了 A 知道的方法 f()(我认为这与 g() 是静态绑定的事实有关)。我期望发生的是得到 B::f(B)。

为什么 f() 在 g() 中的调用是在编译时计算的?

4

4 回答 4

2

A::g不知道这会B引入更多f. 事实上,它选择对 进行虚拟调用f(A*),因为它是那个地方唯一f已知的。

虚拟调度仅由(不可见的)-1st 参数(this)完成,而不是由任何其他参数完成。因此该功能B::f(B*)不参与虚拟链。因此选择了实际的f(A*)——即B::f(A*)——。

调用虚函数并不意味着在运行时选择了最匹配的签名,只有实际的类才是。签名是在编译时选择的(嗯,除了返回类型)。

于 2013-10-18T18:35:09.130 回答
1

重载与虚拟多态性无关。只有A::f(A*)是虚拟的并且是动态调度的。该功能B::f(B*)完全不相关。

于 2013-10-18T18:36:08.673 回答
0

您对基类中的派生类有依赖关系。由于A::g是 的成员,因此它是使用的 vtableA编译的。A它在编译时没有被“计算”。您创建的依赖项查看了 vtable 的错误部分。为了澄清,A只有f(A*)定义。它一无所知f(B*),因此无法调用它。

如果你在真实代码中有这种情况,你真的需要考虑你的设计。

于 2013-10-18T18:36:01.167 回答
0

C++11 在这里添加了override关键字来帮助你处理你的问题。

当您认为某个virtual方法覆盖了基本virtual方法时,请将关键字附加override到它上面。

在这种情况下,当您更改B为包含时:

virtual B* f(B* b) override {
    cout << "B::f(B)" << endl;
    return b;
}

编译器会抱怨并告诉您它B* f(B*)不会覆盖其父级的任何方法。您在这里所做的是引入了一个新的重载,一个完全不同的函数,恰好与方法同名A* f(A*)

具有相同名称的方法参与重载决议,但不会相互覆盖virtual

因为它具有相同的名称,所以您认为它是一个override- 但实际上并非如此。一旦你意识到这B* f(B*)与 无关A* f(A*),其他发生的一切都是完全合理的。

重载决议A检查它对 的选项f(b),只看到一个选项,确定它匹配(因为A*参数与 a 兼容B*),然后调用它。此时virtual检查函数表,override找到正确的(即B::f(A*)唯一的覆盖),然后调用它。

于 2013-10-18T18:39:23.647 回答