5

我是 C++ 新手,我刚刚了解了虚函数。

我听说当我定义一个虚函数时,编译器会生成一个 vtable,其中包含类的虚函数地址。当调用虚函数时,编译器使用 vtable 找到它的地址。

我真的不能理解为什么编译器没有调用对象所属的类实例的函数。

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

4

1 回答 1

10

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

这就是编译器所做的——确保您的程序调用实例所属类的函数。这里的关键词是instance:实例类的知识在编译时是不可用的。

考虑这个简单的例子:

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);
}

请注意棘手的问题:编译器有实例,但它不知道类。实际上,它知道类,但不是最具体的类。编译器在编译时所拥有的知识足以知道有一个名为 的函数howdy,但不足以确定在运行时调用的几种可能性中的哪一种。

这就是 vtables 的用武之地:编译器知道 的子类Dude将有一个指向它们的howdy函数的指针,该指针嵌入到它们的 vtable 中的某个地方。这就是他们在编译时需要知道的全部内容!他们插入一个虚拟调用,在运行时查找函数指针,实现您期望的行为(这种行为的花哨的词是“多态性”)。这是在 ideone 上运行的这个程序的演示。

于 2013-07-16T00:30:24.703 回答