我前面转载的两篇文章基本上把C++中虚函数和多态的概念和用法介绍清楚了,但是编译器底层如何实现多态,是否在所有场合都适合多态呢?看了《程序员》杂志2005年11期上杨喜敏和孟岩的一篇《C++多态技术实现反思》之后,对于多态我又有了更为深入的认识。
一,如何实现多态?
增加一个中间层,在这个中间层中拦截对于方法的调用,然后根据指针所指向的实际对象调用相应的方法实现。在这个过程中,中间层需要完成以下几项工作:
1. 获知方法调用的全部信息,包括被调用的是哪个方法,传入的实际参数有哪些.
2. 获知调用发生时指针(引用)所指向的实际对象
3. 根据第1,2步获得的信息,找到合适的方法实现代码,执行调用。
这里的关键就在于如何在第3步中找到合适的方法实现代码。解决的方法就是对每个对象绑定一个查找表,实现查找表就有两种方法:一种根据名称进行查找,一种根据位置进行查找。
二,两种查找方法的比较
根据名称进行查找:在Smalltalk,Python,Ruby等动态面向对象语言中,实际方法的查找就是根据方法名称进行的。这种查找方法效率较差,但一个突出的优点就是有效空间利用率高。基类中的所有虚方法没有必要在派生类的查找表中全部装载,派生类的查找表只需存放打算改写的那些方法。这样,这种方案的另一个优势,把表中每一项的“方法名”项视为“方法地址”项的描述信息,因为可以认为这种方案中的方法查找表携带自描述信息(或者称为元数据)。基于这种携带自描述信息的数据结构,可以实现丰富多彩的扩展内容,比如在运行时插入新的方法,或者在用户层次上的方法调用截获等。
根据位置进行查找:这是C++采用的查找方法。查找表的结构非常简单,仅仅是一个存放了方法地址的数组。表中的每一项不具备自描述性,只有编译器在编译时知道它们究竟分别对应哪个方法,并且将对于方法的调用代码编译成一个紧凑的指针+偏移的调用硬编码。这种方法的优点就是效率高,基于这种查找表进行方法调用仅仅需要多做一次数组内的随机访问操作。但是这种方法有一个限定,就是要求所有同族多态对象具有完全一样的查找表。也就是说,你必须确保所有实现了某个接口的对象的虚方法查找表的第K项都具有相同的语义,即派生类对象必须具有和基类对象相同结构和长度的查找表。
三,不同的场景应该使用不同的查找方法
并不是所有的场景都适合采用C++的基于绝对位置的查找方法。比如在这样一种应用场景中,对象比较复杂,特性稠密,行为变化多端,用一族系中兄弟对象数量庞大,而彼此之间大同小异。此对象中的虚方法数量多,而改写率低。GUI系统和视频游戏系统是这种应用场景的典型代表。对于这种场景,使用C++的查找方法就不太合适了,为了一点小小的改动可能就会浪费大量的资源空间。基于这个原因,很多框架都避开了C++这种查找方法,而是自己实现了特定的查找方法,比如MFC,VCL,Qt等。
更加详细的介绍请大家参阅《程序员》2005年第11期上的文章。
![](https://img2024.cnblogs.com/blog/35695/202408/35695-20240826070647288-243310440.jpg)