1.3.3 多态支持
多态指的是一个接口多种实现,同一接口调用可以根据对象调用不同的实现,产生不同的执行结果。多态有两种形式,一种是静态多态,另一种是动态多态。
静态多态也称为函数重载(overlap)。在早期的C语言中,每个函数的名字都不相同,所以可以直接通过函数名唯一地确定函数。例如,在CShape中有两个函数名字相同的setCenter,所以不能通过函数名来唯一地确定函数。编译器采用的方法是对函数名进行编码(称为name mangling),编码的规则不同,编译器的实现也不同,原则是把函数名、参数个数、参数类型等信息编码成唯一的一个函数名(也称为函数的签名)。在Linux中对上述文件进行编译,然后可以通过nm命令查看编译后的函数签名。可以得到两个不同的函数签名,分别为:
- _ZN6CShape9setCenterE6CPoint,对应成员函数setCenter(CPoint point)。
- _ZN6CShape9setCenterEdd,对应成员函数setCenter(double xAxis, double yAxis)。
关于Name Mangling的具体编码规则,可以参考其他书籍或文章。
动态多态也称为函数重写(override),该机制主要通过虚函数实现。编译器对于虚函数的实现主要通过增加虚函数指针和虚函数表的方式来实现。编译器会在数据段中增加一个数据空间,称为虚函数表,虚函数表中存放的是编译后函数的地址,同时在类的构造函数中把实例化对象的虚指针指向虚函数表。CShape示例化对象的布局如图1-10所示。
图1-10 CShape示例化对象布局
CCircle示例化对象的布局如图1-11所示。
图1-11 CCirle示例化对象布局
从编译器的角度来看,当CCircle重写了CShape的虚函数(此处为getType),编译器会在CCircle对应的虚函数表中修改函数的地址,此函数的地址为CCircle中函数的地址。若CCircle仅仅继承CShape的虚函数,但并没有重写,则CCircle的虚函数表中函数的地址仍然指向CShape中函数的地址。
另外,在图1-10和图1-11中都指出虚函数表(vtbl)位于数据段中,这样设计主要是因为使用该数据时只需要读权限,而不需要执行权限。但这并不意味着虚函数表会动态地变化,实际上虚函数表在编译时唯一确定,在程序执行过程中并不会变化。
编译器支持封装、继承和多态的特性以后,也会按照与C语言一样的方式生成可执行文件,并且也按照对应的调用约定支持函数调用。