C++多态技术的实现和反思

面向对象技术最早出现于1960年代的Simula 67系统,并且在1970年代保罗阿托实验室开发的Smalltalk系统中发展成熟。然而对于大部分程序员来说,C++是第一个可用的面向对象程序设计语言。因此,我们关于面向对象的很多概念和思想直接来自于C++。但是,C++在实现面向对象中关键的多态性时,选择了与Smalltalk完全不同的方案。其结果是,尽管在表面上两者都实现了相似的多态性,但是在实践中却有着巨大的区别。具体的说,C++的多态性实现更加高效,但是并不适用于所有场合。很多经验不足的C++开发者不明白这个道理,在不合适的场合强行使用C++的多态性机制,落入削足适履的陷阱而不能自拔。本文将详细探讨C++多态性技术的局限性及解决的办法。

两种不同虚方法调用实现技术

C++的多态性是C++实现面向对象技术的基础。具体的说,通过一个指向基类的指针调用虚成员函数的时候,运行时系统将能够根据指针所指向的实际对象调用恰当的成员函数实现。如下所示:

class Base {
   public:
   virtual void vmf() { ... }
   };
   class Derived : public Base {
   public:
   virtual void vmf() { ... }
   };
   Base* p = new Base();
   p->vmf(); // 这里调用Base::vmf
   p = new Derived();
   p->vmf(); // 这里调用
// Derived::vmf
   ...


请注意代码中突出注释的两行,虽然其表面语法完全相同,但是却分别调用了不同的函数实现。所谓的“多态”即就此而言。这些知识是每一个C++开发者都熟知的。

现在我们假设自己是语言的实现者,我们应当如何来实现这种多态性?稍加思考,我们不难得到一个基本的思路。多态性的实现要求我们增加一个间接层,在这个间接层中拦截对于方法的调用,然后根据指针所指向的实际对象调用相应的方法实现。在这个过程中我们人为增加的这个间接层非常重要,它需要完成以下几项工作:

1. 获知方法调用的全部信息,包括被调用的是哪个方法,传入的实际参数有哪些。

2. 获知调用发生时指针(引用)所指向的实际对象。

3. 根据第1、2步获得的信息,找到合适的方法实现代码,执行调用。

这里的关键在于如何在第3 步中找到合适的方法实现代码。由于多态性是就对象而言的,因此我们在设计时要把合适的方法实现代码与对象绑定到一起。也就是说,必须在对象级别实现一个查找表结构,根据1、2步获得的对象和方法信息,在这个查找表中找到实际的方法代码地址,并加以调用。现在问题变成了,我们应当根据什么信息进行方法查找。对于这个问题有两个不同的解决思路,一个是根据名称进行查找,另一个是根据位置进行查找。粗看上去这两种思路似乎没什么大的差别,但是在实践中,这两种不同的实现思路导致了巨大的差别。下面我们详细地加以考察。

在Smalltalk、Python、Ruby等动态面向对象语言中,实际方法的查找是根据方法名称进行的,其查找表结构如下:

由于这种查找表根据方法的名称进行方法查找,因此在查找过程中涉及字符串比较,效率较差。但是这种查找表有一个突出的优点,就是有效空间利用率高。为了说明这一点,我们假设一个基类Base中有100个方法可供派生类改写(因此所有Base对象所共享的方法查找表有100项),而它的一个派生类Derived仅仅只打算改写其中5个方法,那么Derived类对象的方法查找表只需要5项。当一个方法调用发生的时候,runtime根据被调用的方法名称在这个长度为5 的方法查找表中进行字符串查找,如果发现该方法在查找表中,则执行调用,否则将调用转寄(forward)给Base类执行。这是虚方法调用的标准行为。当派生类实际改写的方法数量很少的时候,可以将查找表安排成线性表,查找时顺序比较,这种情况下有效空间利用率达到100%。如果派生类实际改写的方法数量较多,那么可以采用散列表,如果采用合理的散列函数,同样可以在空间利用率很高(一般可接近75%).. 的情况下实现方法的快速查找。应当注意到,由于编译器可以很容易地获得所有被改写方法的名称,因此可以执行标准的gperf算法获得最优的散列函数。

时间: 2024-12-31 18:54:57

C++多态技术的实现和反思的相关文章

C++多态技术

摘要 本文描述了C++中的各种多态性.重点阐述了面向对象的动态多态和基于模板的静态多态,并初步探讨了两种技术的结合使用. 关键词 多态 继承 虚函数 模板 宏 函数重载 泛型编程 泛型模式 导言 多态(polymorphism)一词最初来源于希腊语polumorphos,含义是具有多种形式或形态的情形.在程序设计领域,一个广泛认可的定义是"一种将不同的特殊行为和单个泛化记号相关联的能力".和纯粹的面向对象程序设计语言不同,C++中的多态有着更广泛的含义.除了常见的通过类继承和虚函数机制

深入挖掘Windows脚本技术

window|脚本 [目录]1,前言2,回顾WSH对象3,WMI服务4,脚本也有GUI5,反查杀6,来做个后门7,结语8,参考资料 [前言]本文讲述一些Windows脚本编程的知识和技巧.这里的Windows脚本是指"Windows Script Host"(WSH Windows脚本宿主),而不是HTML或ASP中的脚本.前者由Wscript或Cscript解释,后两者分别由IE和IIS负责解释.描述的语言是VBScript.本文假设读者有一定的Windows脚本编程的基础.如果你对

C++ 多态分析

貌似公司面试都喜欢问多态,今天做个总结记录. 1.什么是多态 多态就是Polymorphism,一个接口的多种实现.在不同的上下问下,接口的实现表现出不同的特征. 2.多态的好处 多态带来两个明显的好处:一是不用记大量的函数名了,二是它会依据调用时的上下文来确定实现.确定实现的过程由C++本身完成另外还有一个不明显但却很重要的好处是:带来了面向对象的编程. 3.多态的实现 函数重载,宏多态,模板函数,虚函数. 3.1函数重载(function overloading) 不同的参数列表,不同的返回

首席技术官们别担忧:你们依然掌握着技术支出

在数字时代,企业高管对企业技术的获取的参与度越来越高.但是首席信息官依然对多数技术的采购具有最终决定权. 由于技术实现的复杂性,首席信息官们控制着多数IT企业的钱袋.根据Forrester Research和CompTIA的新报告,其他业务主管在获取数字工具方面发挥了越来越大的作用,强调了技术日益重要. 要确定企业高管在何种程度上购买技术或影响技术采购是困难的.然而,据Forrester Research的分析师Andrew Bartels,报告<高管级别的技术采购模式>的主要作者的观点,可以

五种技术悖论与发展政治学

随着技术变得更强大,其消极作用也变得难以令人视而不见,我们能够建造一种技术的政治学来顺应这种史无前例的状况吗? 文︱安德鲁·芬伯格(加拿大) 海德格尔问道:鸟儿是因为拥有翅膀才会飞翔呢,还是因为会飞翔才拥有翅膀?这个问题看似愚蠢,却为技术和发展的反思提供了一个独特的切入点. 鸟儿显然配备着翅膀,这也可以解释他们能够飞翔的原因.这是对于海德格尔问题的常识性回答.但这个答案的含意还不够明确.尽管直觉告诉我们,鸟儿属于天空,但我们的语言似乎却在说,它们与其作用的环境是脱离的,甚至与其用于应对那种环境的

云扩展,第 3 部分: 探索云中的视频分析

使用各种方法.工具和系统设计执行视频和图像分析.监视和安全保护 探索和分析通过云扩展来执行视频和图像分析的方法.工具和系统设计.本系列之前的文章已经介绍过,与传统的以计算为中心.可伸缩.高性能计算相比,视频分析需要一种更加平衡的"以数据为中心的计算架构".作者介绍了如何将 OpenCV 和类似工具用于数字视频分析和方法,以便使用集群和分布式系统设计来扩展此类分析. 前几期文章中讨论了专为视频分析和新的 OpenVX 硬件加速而设计的协处理器,可将它应用到本文中提供的计算机视觉 (CV)

面向对象编程中类设计的几个原则

1. 开闭原则(the Open Closed Principle OCP) 一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的.因此在进行面向对象设计时要尽量考虑接口封装机制.抽象机制和多态技术.该原则同样适合于非面向对象设计的方法,是软件工程设计方法的重要原则之一.我们以收音机的例子为例,讲述面向对象的开闭原则.我们收听节目时需要打开收音机电源,对准电台频率和进行音量调节.但是对于不同的收音机,实现这三个步骤的细节往往有所不同.比如自动收缩电台的收音机和按钮式收缩在操作细节上并不相同

java IO之 字符流 (字符流 = 字节流 + 编码表) 装饰器模式

字符流 计算机并不区分二进制文件与文本文件.所有的文件都是以二进制形式来存储的,因此, 从本质上说,所有的文件都是二进制文件.所以字符流是建立在字节流之上的,它能够提供字符 层次的编码和解码.列如,在写入一个字符时,Java虚拟机会将字符转为文件指定的编码(默认 是系统默认编码),在读取字符时,再将文件指定的编码转化为字符. 常见的码表如下: ASCII:           美国标准信息交换码.用一个字节的7位可以表示. ISO8859-1:   拉丁码表.欧洲码表,用一个字节的8位表示.又称

怎样很好的将java中的构造重载学好(初学者)

问题描述 怎样很好的将java中的构造重载学好(初学者) 现在初学,但是在写代码的时候总是无法使用构造重载这种方法,并且在写代码的时候老是思绪混乱应该怎莫办??????? 解决方案 java允许在一个类中,多个方法拥有相同的名字,但在名字相同的同时,必须有不同的参数,这就是重载,编译器会根据实际情况挑选出正确的方法,如果编译器找不到匹配的参数或者找出多个可能的匹配就会产生编译时错误,这个过程被称为重载的解析 1 普通方法的重载 普通方法的重载是Java实现多态技术的重要手段,为编程带来了很多便利