为什么使用虚拟表代替函数指针

Why use a virtual table instead of function pointers?

本文关键字:函数 指针 虚拟 为什么      更新时间:2023-10-16

我是C 的初学者,我不明白为什么C 使用虚拟表来实现多态性而不是简单的功能指针。

让我们考虑一个例子:

struct Shape {
    virtual double area() = 0;
};
struct Square: public Shape {
    virtual double area();
    /* ... */
};
struct Circle: public Shape {
    virtual double area();
    /* ... */
};
double f(Shape &s) { return 42.0 + s.area(); }

C 通常(总是?)如何实现多态性:Shape和每个派生的类都有隐藏的指针作为虚拟表作为第一个元素。当调用s.area()时,指向相应的area功能的指针取自s的虚拟表。

但是,如果我不知道C 和虚拟表,并且想在C中意识到这种多态性,我会这样这样做:

struct Shape {
    double (*area)(struct Shape *this);
};
struct Square {  /* is a Shape */
    double (*area)(struct Square *this);
    /* ... */
};
struct Circle {  /* is a Shape */
    double (*area)(struct Circle *this);
    /* ... */
};
double f(struct Shape *s) { return 42.0 + s->area(s); }
double cirlce_area(struct Circle *this);
double square_area(struct Square *this);
/* ... */
struct Circle c = { &circle_area, ... };
printf("%f", f((struct Shape *)c));

调用s->area()时,使用s结构的第一个指针。

为什么C 使用虚拟表,而不是仅将指针放在结构开头的虚拟函数上(使用相同的顺序)?我目前找不到任何原因可能无法正常工作的原因。

概念上,这是同一回事。

虚拟方法表不过是函数指针列表。它没有集成在对象本身中,因为它对于对象类是静态的,因此可以在同一类的实例之间共享(在初始化期间保存计算能力和整个对象期间的内存)。

在C 中,每个构造函数都将虚拟方法表指针设置为其所属类的指针。因此,在最外面的构造函数运行之后,虚拟方法解析为对象为。

考虑包含许多虚拟方法的类将发生的事情:您必须为每个实例分别支付存储x函数pointer 的成本。这可能很快变得非常浪费。

使用VPTR还可以帮助实现其他语言功能:易于使用VPTR的值来确定对象的运行时类型(想想typeid)。