【问题标题】:Why use a virtual table instead of function pointers?为什么使用虚拟表而不是函数指针?
【发布时间】:2014-04-12 12:46:50
【问题描述】:

我是 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()时,从s的虚表中取出对应area函数的指针。

但是如果我不了解 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++ 标准不要求使用指向虚拟表的指针 - 实现也可以使用哈希表,或将函数指针直接放入对象中,就像您刚才所做的那样。
  • 当你有很多这样的函数指针时,即使在 C 语言中,你也会把它们都放在一个不同的结构中,只保留一个指向它的指针。这实际上在 Linux 内核中是非常惯用的。
  • 不只是 Linux 内核,Microsoft COM 也是如此。但考虑到 Linux 不喜欢 C++,我想 Linux 使用如此常见的 C++ 模式更令人惊讶。

标签: c++ polymorphism virtual-functions


【解决方案1】:

从概念上讲,这是一回事。

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

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

【讨论】:

    【解决方案2】:

    考虑一个包含许多虚方法的类会发生什么:您将不得不支付为每个实例单独存储 X 函数指针的成本。这很快就会变得非常浪费。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-26
      • 1970-01-01
      • 2010-10-13
      相关资源
      最近更新 更多