为什么使用虚拟表而不是函数指针?

时间:2014-03-09 13:32:11

标签: c++ polymorphism virtual-functions

我是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 ++使用虚拟表而不是仅仅在结构的开头放置指向虚函数的指针(具有相同的顺序)?我目前无法找到任何理由说明最后一种方法可能无效。

2 个答案:

答案 0 :(得分:3)

从概念上讲,这是完全相同的事情。

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

在C ++中,每个构造函数都将Virtual Method Table指针设置为它所属的类的指针。因此,在最外层构造函数运行后,虚方法将解析为对象所在的类。

答案 1 :(得分:2)

考虑包含许多虚方法的类会发生什么:您必须为每个实例分别存储X函数指针的成本。这很快就会变得非常浪费。

使用vptr还可以帮助实现其他语言功能:可以很容易地使用vptr的值来确定对象的运行时类型(考虑typeid)。