Vtable 是否仅与指向基类的指针一起使用

Eng*_*999 2 c++ polymorphism vtable

我知道这里有很多关于 vtable 的问题,但我仍然有点困惑。

仅当我们有指向基类的指针来解析要调用派生类的哪个虚函数时,才使用虚函数表吗?

在下面的示例中,在情况 1 中,即使 Tiger 对象不是在堆/自由存储上动态创建的,这里是否在运行时使用 vtable?

在情况 2 中,即使编译器在编译时知道我们指向 Tiger 对象,也使用了 vtable。

案例3呢?

提前致谢。

#include <iostream>

using namespace std;

class Animal // base class
{
    public:
        virtual void makeNoise() {cout<<"  "<<endl;}
};

class Tiger: public Animal
{
    public:
        void makeNoise() {cout<<"Tiger Noise"<<endl;}
};

class Elephant: public Animal
{
    public:
        void makeNoise() {cout<<"Elephant Noise"<<endl;}
};

int main()
{
    //case 1
    Tiger t1;
    Animal* aptr = &t1;
    aptr->makeNoise(); // vtables used?

    //case 2
    Tiger t2;
    Tiger* tptr = &t2;  //vtables used ?
    tptr->makeNoise();

    //case 3
    Elephant e1;       //vtables used ?
    e1.makeNoise();

}
Run Code Online (Sandbox Code Playgroud)

Rei*_*ica 5

特定编译器是否使用虚函数表或完全不同的机制来实现动态虚函数分派取决于该编译器的内部实现。如果您想要特定编译器行为的答案,请查阅该编译器的文档和/或源代码。

C++ 语言本身定义了虚函数调用必须如何工作,并将其留给编译器来实现。

标准要求的是,根据调用函数的对象的动态类型,将虚拟函数的调用分派给最终的重写器。t1在您的代码中,和 的动态类型t2Tiger,并且 的动态类型e1Elephant

是的,大多数(如果不是全部)编译器使用虚函数表来实现虚函数调用。是的,任何像样的编译器都应该在能够做到的情况下最大限度地尝试在编译时解决动态调度问题,并在可以的情况下用直接调用替换虚拟表使用(这是编译器的实现质量问题) )。

您的示例中的哪些调用将被静态调度取决于编译器的优化器的“积极性”(或“智能”,如果您愿意)。

我想说,每个理智的编译器都应该通过 静态分派调用e1,即使禁用了优化。在那里调用动态调度机制是完全不必要的悲观情绪。

至于通过aptr和 的调用tptr,这取决于编译器优化器的静态分析器是否能够消除aptrtptr,并使用它们指向的实际对象替换它们(因为该信息在编译时可用)。一个好的优化器应该能够做到这一点并静态地调度所有 3 个调用。

要确定编译器如何处理调用,请检查生成的程序集。