C++中虚函数的原理

cra*_*udy 5 c++ oop

我是C++的新手,我刚刚学习了虚函数.

我听说编译器在定义虚函数时会生成一个包含类的虚函数地址的vtable.调用虚函数时,编译器使用vtable查找其地址.

我无法理解编译器没有调用该对象所属的类实例的函数的原因.

为什么编译器使用vtable?你能解释一下吗?

das*_*ght 9

我真的不明白为什么编译器没有调用类实例属于的函数?

这就是编译器的作用 - 确保程序调用实例所属类的函数.这里的关键词是实例:实例类的知识在编译时不可用.

考虑这个简单的例子:

struct Dude {
    virtual void howdy() = 0;
};
struct Bob : public Dude {
    virtual void howdy() { cout << "Hi, Bob!" << endl; }
};
struct Moe : public Dude {
    virtual void howdy() { cout << "Hi, Moe!" << endl; }
};
// Note the pass by reference below: passing by reference or by pointer,
// but not by value, is important for achieving polymorphic behavior.
void say_hi(Dude& dude) {
    dude.howdy(); // <<== Here is the tricky line
}
int main(int argc, char* argv[]) {
    Bob b;
    Moe m;
    Dude *d = rand() & 1 ? (Dude*)&b : &m;
    say_hi(*d);
}
Run Code Online (Sandbox Code Playgroud)

注意棘手的问题:编译器有实例,但它不知道类.实际上,它有点了解课程,但不是最具体的课程.编译器在编译时具有的知识足以知道有一个函数被调用howdy,但不足以决定在运行时调用哪一种可能性.

这就是vtables拯救的地方:编译器知道子类Dude将有一个指向其howdy功能的指针嵌入到他们的vtable中.这就是他们在编译时需要知道的一切!它们插入一个虚拟调用,在运行时查找函数指针,实现您期望的行为(这种行为的奇特单词是"多态").这是在ideone上运行的这个程序演示.

  • 您的示例是正确的但可能并不理想:我知道至少有一个编译器会在编译时解析调用.更经典的`Base*p; if(rand()&1)p = new Derived1; else p = new Derived2; p-> do_something();`略胜一筹,因为它避免了"问题",而且在我看来,无论如何都更具说明性. (3认同)