虚表 C++

23

我看到很多人写道:“对于一个声明了虚函数的类,存在一个虚拟表。”

我的问题是,虚拟表只存在于声明有虚函数的类中吗?还是存在于从该类派生出来的类中?

例如:

class Base{
    public:
        virtual void print(){cout<<"Base Print\n";}
};
class Derived:public Base{
    public:
        void print(){cout<<"Derived print\n";}
};

//From main.cpp 
Base* b = new Derived;
b->print();

问题:如果类派生没有虚函数表,那么输出就不会是“derived print”。因此,在任何声明了虚函数的类和从该类继承的类中都存在虚函数表。这个理解正确吗?


2
为了完成实验,请创建一个派生自derived的derived2类,并覆盖print方法。通过指向基类的指针调用此类的实例上的print方法。 - VoidPointer
2
请点击以下链接了解有关Vtable的更多信息:http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/ - user537597
4个回答

20

就虚函数特定的功能而言,在传统的虚函数表(vtable)实现方法中,只有当派生类至少覆盖了一个虚函数时,该派生类才需要一个单独的vtable版本。在您的示例中,Derived 覆盖了虚函数 print。由于 Derived 有自己的 print 版本,因此在 Derived vtable 中对应的条目与 Base vtable 中的不同。这通常需要为 Derived 创建一个单独的vtable。

如果 Derived 根本没有覆盖任何内容,则从形式上讲,它仍然是一个单独的多态类,但是为了使其虚函数正常工作,我们可以简单地重用 Base 的vtable 来作为 Derived 的vtable。因此,从技术上讲,对于 Derived,不存在任何需要单独vtable的必要。

然而,在实际的实现中,我们通常所指的“vtable”数据结构还保存了一些额外的类特定信息。这些额外的信息非常与类相关,以至于大多数情况下即使它们使用相同的虚函数集合,不同类之间也无法共享vtable。例如,在某些实现中,存储在每个多态对象中的vtable指针指向的数据结构还存储了有关类的所谓“RTTI”信息。因此,在大多数(如果不是所有)实际实现中,即使存储在这些表中的虚函数指针是相同的,每个多态类仍然会有其自己的vtable。


1
@AndreyT:您知道一些好的介绍C ++中vtable相关概念的网页吗?我正在尝试弄清楚为什么需要vtable以及它的确切实现方式。 - Lazer
@Lazer的实现细节未指定。表格是必需的,因此在运行时系统知道调用哪个多态函数的版本(这不能在编译时确定;考虑对基类指针进行操作 - 如果指针指向派生类或基类的实例,则操作可能不同)。请参见https://en.wikipedia.org/wiki/Virtual_method_table。 - RJFalconer

3

是的,这是真的。实际上,基本定义如下:

class derived:public base{
public:
 void print(){cout<<"derived print\n";}
};

完全等同于:

class derived:public base{
public:
 virtual void print(){cout<<"derived print\n";}
};

因为你已经在基类中将print定义为虚函数了。

我希望编译器能够强制执行这一点...


很奇怪的是,编译器可以将一个具有 void print() 的基类和一个具有 virtual void print() 的派生类编译在一起。 - user6547518

3

是的,您的理解是正确的。任何具有带有虚函数的基类的类都有一个虚表。


2
是的,这是正确的。一个类继承其基类的所有数据成员,包括虚表。然而,虚表条目会相应地进行调整(例如,如果类覆盖了基类的虚方法,则虚表中对应的条目必须指向它自己的实现)。
但请记住,“虚表”概念是几乎每个编译器都使用的常见做法,但它并不是强制性的,也没有标准化。

vtable不是成员或类似成员。你是指vptr吗? - curiousguy

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