网上有很多关于VTables的资源。他们通常对此有相同的陈述:
“每当类本身包含虚函数或覆盖父类的虚函数时,编译器就为该类构建一个vtable。这意味着并非所有类都有编译器为它们创建的vtable。 vtable包含指向该类中虚函数的函数指针。每个类只能有一个vtable,同一个类的所有对象将共享相同的vtable。“
那么为什么这意味着并非所有类都有编译器为它们创建的vtable?是因为somc类没有虚函数吗?
答案 0 :(得分:7)
正。有些类没有vtable,因为它们没有任何虚拟方法。
虚方法是编译器无法生成直接调用的方法,因为它根据类的实现而有所不同。 Vtables是一种查找表,可以通过延迟在程序运行期间调用哪个实现的决定来解决这个问题:编译器生成方法查找而不是生成函数调用,然后调用返回的方法
举个例子:
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具有显着的副作用,即禁用 r un t ime t ype i 信息。如果您需要在代码中使用 RTTI ,则您的类必须至少具有一个虚拟方法。惯例是在这些情况下将析构函数标记为虚拟。
答案 1 :(得分:5)
实际上,C ++中没有任何内容要求任何类都有vtable - 这完全是一个实现问题。但是,具有虚函数的类必须以某种方式支持多态函数调用,并且这将始终需要某种类型的表/映射。此表/映射将由编译器为具有多态函数的类创建,并且可能(根据编译器质量)为不具有多态函数的类创建。
答案 2 :(得分:2)
此外,某些类没有vtable,因为已明确删除,请参阅__declspec(novtable)
(特定于编译器)