1、C+ C+ 虚函数表解析虚函数表解析 前言前言 C+中 的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用 父类型别的指针指向其子类的实例, 然后通过父类的指针调用实际子类的成员函 数。这种技术 可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛 型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术, RTTI 技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时 决议。 关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的 C+ 的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家 一个清晰的剖 析。 当然, 相同的文章在网上也出
2、现过一些了, 但我总感觉这些文章不是很容易阅读, 大段大段的代码,没有图片,没有详细的说明,没有比较,没有举一反三。不利 于学习和阅读,所以这是我想写下这篇文章的原因。也希望大家多给我提意见。 言归正传,让我们一起进入虚函数的世界。 虚函数表虚函数表 对 C+ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表 (Virtual Table)来实现的。简称为 V-Table。 在这个表中,主是要一个类的虚 函数的地址表, 这张表解决了继承、 覆盖的问题, 保证其容真实反应实际的函数。 这样,在有虚函数的类的实例中这个表被分配在了 这个实例的内存中,所以, 当我们用父
3、类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了, 它就像一个地图一样,指明了实际所应该调用的函数。 这里我们着重看一下这张虚函数表。C+的编译器应该是保证虚函数表的指针存 在于对象实例中最前面的位置 (这是为了保证取到虚函数表的有最高的性能 如果有多层继承或是多重继承的情况下)。 这意味着我们通过对象实例的地址 得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。 听我扯了那么多, 我可以感觉出来你现在可能比以前更加晕头转向了。 没关系, 下面就是实际的例子,相信聪明的你一看就明白了。 假设我们有这样的一个类: class Base public: virtual vo
4、id f() cout f(); /Derive:f() b2-f(); /Derive:f() b3-f(); /Derive:f() b1-g(); /Base1:g() b2-g(); /Base2:g() b3-g(); /Base3:g() 安全性安全性 每次写 C+的文章,总免不了要批判一下 C+。这篇文章也不例外。通过上面的 讲述,相信我们对虚函数表有一个比较细致的了解了。水可载舟,亦可覆舟。下 面,让我们来看看我们可以用虚函数表来干点什么坏事吧。 一、通过父类型的指针访问子类自己的虚函数一、通过父类型的指针访问子类自己的虚函数 我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要 基于函数重载的。虽然在上面的图中我们可以看到 Base1 的虚表中有 Derive 的 虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数: Base1 *b1 = new Derive(); b1-f1(); /编译出错 任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数未覆盖父类的成员函数的行为都