所有的类都会由编译器创建一个Vtable吗?

6
有很多关于VTables的在线资源。它们通常有相同的声明:
“每当一个类本身包含虚函数或者重写父类的虚函数时,编译器会为该类构建一个vtable。这意味着并非所有类都由编译器创建vtable。vtable包含指向该类虚函数的函数指针。每个类只能有一个vtable,同一类的所有对象将共享相同的vtable。”
那么这究竟意味着什么,为什么不是所有类都由编译器创建vtable?是因为某些类没有虚函数吗?
3个回答

7

确切地说,有些类没有虚函数表(vtable),因为它们没有任何虚方法。

虚方法是指编译器无法生成直接调用的方法,因为它取决于类的实现方式。虚函数表是一种查找表,通过延迟程序运行时决定要调用哪个实现来解决这个问题:编译器不会生成函数调用,而是在vtable上生成方法查找,然后调用返回的方法。

举个例子:

class Foo
{
public:
    virtual void vMethod()
    {
        std::cout << "Foo::vMethod was called!" << std::endl;
    }
};

class Bar : public Foo
{
public:
    virtual void vMethod()
    {
        std::cout << "Bar::vMethod was called!" << std::endl;
        std::cout << "This is not the same as Foo::vMethod." << std::endl;
    }
};

Foo* foo = new Bar;
foo->vMethod();

这将打印出 Bar 的消息。在大多数非平凡的情况下,编译器无法提前知道调用虚拟方法的对象类型。如上所述,vtable通过提供统一的查找机制来查找方法实现解决了这个问题,无论对象的类型如何。
每个类的实例中必须存在一个vtable指针(需要额外的指针大小内存,可能为4或8个字节),以及程序地址空间中某些微不足道的静态内存。这对您来说可能不是很多(确实许多人会同意),但在某些场景中可能会很麻烦(例如内存极其有限的嵌入式系统)。为每个类都添加vtable将违反C ++的普遍原则,即您只需支付使用的内容,因此如果不必生成vtable,则编译器不会生成任何vtable。
没有vtable的显着副作用是禁用运行时类型信息。如果需要在代码中使用RTTI,则类必须至少有一个虚拟方法。惯例是在这些情况下将析构函数标记为虚拟的。

5

实际上,C++中没有任何要求任何类都必须有虚表 - 这完全是一个实现问题。然而,拥有虚函数的类必须以某种方式支持多态函数调用,这将始终需要某种形式的表/映射。对于具有多态函数的类,编译器将为其创建此表/映射,并且根据编译器质量,可能会为那些没有多态函数的类创建。


3
您知道有没有任何一款C++编译器(无论质量如何)会为没有多态函数的类创建虚表(或其他类似的结构)吗? - Jerry Coffin
1
我不这样做,有很好的理由:要实现extern "C",C++编译器必须已经支持没有虚函数表的结构体。最好只在需要时创建虚函数表以提高效率。 - MSalters

2
此外,一些类没有虚函数表是因为它被明确地移除了,参见__declspec(novtable)(编译器特定)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接