派生对象上的C++虚函数调用是否通过vtable?

aaa*_*aaa 7 c++ virtual

在下面的代码中,它通过指向派生对象的指针调用虚函数foo.这个电话会通过vtable还是B::foo直接打电话?

如果它是通过vtable进行的,那么B::foo直接调用它的C++惯用方法是什么?我知道在这种情况下我总是指着一个B.

Class A
{
    public:
        virtual void foo() {}
};

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


int main()
{
    B* b = new B();
    b->foo();
}
Run Code Online (Sandbox Code Playgroud)

Ben*_*igt 9

如果启用了优化,大多数编译器都会足够智能,以消除该场景中的间接调用.但只是因为你刚刚创建了对象并且编译器知道动态类型; 可能存在您知道动态类型而编译器不知道的情况.

  • @Billy:我不太确定.`static_cast`只是告诉编译器动态类型是`B`的(非严格)子类,而不是它是'B`,所以优化不适用于IMO. (3认同)

unq*_*ind 6

像往常一样,这个问题的答案是"如果它对你很重要,那就看看发出的代码".这就是g ++在没有选择优化的情况下生成的:

18     b->foo();
0x401375 <main+49>:  mov    eax,DWORD PTR [esp+28]
0x401379 <main+53>:  mov    eax,DWORD PTR [eax]
0x40137b <main+55>:  mov    edx,DWORD PTR [eax]
0x40137d <main+57>:  mov    eax,DWORD PTR [esp+28]
0x401381 <main+61>:  mov    DWORD PTR [esp],eax
0x401384 <main+64>:  call   edx
Run Code Online (Sandbox Code Playgroud)

这是使用vtable.直接调用,由以下代码生成:

B b;
b.foo();
Run Code Online (Sandbox Code Playgroud)

看起来像这样:

0x401392 <main+78>:  lea    eax,[esp+24]
0x401396 <main+82>:  mov    DWORD PTR [esp],eax
0x401399 <main+85>:  call   0x40b2d4 <_ZN1B3fooEv>
Run Code Online (Sandbox Code Playgroud)


rob*_*ert 4

是的,它将使用 vtable(只有非虚拟方法绕过 vtable)。如需直接致电B::foo()b请致电b->B::foo()

  • 对于问题中的代码,大多数优化编译器不仅不会使用 v 表,而且大多数都会内联空主体,并且 v 表本身可能会被链接器消除,因为它没有被使用。 (4认同)
  • @Ben Voigt 是的,这很有可能。我认为原始发布者正在查看的代码要复杂得多,但情况可能并非如此。 (2认同)