(如果你没有看过“Inside The C ++ Object Model”这本书,但又急切想知道,那你就应该从这里开始)
虚函数是如何做到因对象的不同而调用其相应的函数的呢?现在我们就来剖析虚函数。我们先定义两个类
1
2
3
4
五
6
7
8
9
10
|
class
A{
//虚函数示例代码
public
:
virtual
void
fun(){cout<<1<<endl;}
virtual
void
fun2(){cout<<2<<endl;}
};
class
B :
public
A{
public
:
void
fun(){cout<<3<<endl;}
void
fun2(){cout<<4<<endl;}
};
|
由于这两个类中有虚函数存在,所以
compile-器
就会为他们两个分别插入一段你不知道的数据,并为他们分别创建一个表。那段数据叫做vptr的
指针
,指向那个表。那个表叫做VTBL,每个类都有自己的VTBL,VTBL的作用就是保存自己类中虚函数的地址,我们可以把VTBL地形象看成一个
数组
,这个数组的每个元素存放的就是虚函数的地址,请看图
A * p =新A;
P->乐趣();
毫无疑问,调用了A ::乐趣(),但是A ::乐趣()是如何被调用的呢?它像普通函数那样直接跳转到函数的代码处吗?不,其实是这样的,首先是取出的vptr的值,这个值就是vtbl中的地址,再根据这个值来到VTBL这里,由于调用的函数A ::乐趣()是第一个虚函数,所以取出的vtbl中第一个插槽的值即为第一个虚函数的地址(在不同的编译器下第一个VTBL布局不完全相同,在VS2008中第一个插槽的值为指向第一个虚函数的指针,而其他编译器中也可能出现第一个插槽中值为TYPE_INFO对象的指针),这个值就是A ::乐趣()的地址了,最后调用这个函数。现在我们可以看出来了,只要vptr的不同,指向的vtbl中就不同,而不同的VTBL里装着对应类的虚函数地址,所以这样虚函数就可以完成它的任务。
而对于class A和class B来说,他们的vptr
指针
存放在何处呢?其实这个指针就放在他们各自的实例对象里。由于class A和class B都没有
数据成员
,所以他们的实例对象里就只有一个vptr的指针。通过上面的分析,现在我们来实作一段代码,来描述这个带有虚函数的类的简单模型。
1
2
3
4
五
6
|