我是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 ++使用虚拟表而不是仅仅在结构的开头放置指向虚函数的指针(具有相同的顺序)?我目前无法找到任何理由说明最后一种方法可能无效。
答案 0 :(得分:3)
从概念上讲,这是完全相同的事情。
虚方法表不是函数指针列表。它没有集成在对象本身中,因为它对于对象的类是静态的,因此它可以在同一个类的实例之间共享(在对象整个生命周期中保存初始化期间的计算能力和内存)。
在C ++中,每个构造函数都将Virtual Method Table指针设置为它所属的类的指针。因此,在最外层构造函数运行后,虚方法将解析为对象所在的类。
答案 1 :(得分:2)
考虑包含许多虚方法的类会发生什么:您必须为每个实例分别存储X函数指针的成本。这很快就会变得非常浪费。
使用vptr还可以帮助实现其他语言功能:可以很容易地使用vptr的值来确定对象的运行时类型(考虑typeid
)。