大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C++技巧 > C++多态技术的实现和反思

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

关键词:c++多态  阅读(1068) 赞(12)

[摘要]本文是对C++多态技术的实现和反思的讲解,希望对您学习C++程序设计有所帮助。

两种不同的多态性运用场景

  学习过数值剖析的读者应该熟知,在矩阵运算的电算求解范围,低阶稀疏矩阵的求解与高阶稀疏矩阵的求解是性质完全不同的两个效果,其存储方案和求解算法如出一辙。十分幽默的是,在多态性的理论运用中,也有着与矩阵效果相似的两种性质上如出一辙的场景。

  第一种场景中,我们所结构的对象比拟复杂,同一族系中兄弟类总数不多,而彼此之间的差别较大,因而对象中的虚办法数量少,而改写率高。我们通常在教科书上所接触的面向对象例子,以及在普通运用范围中接触的对象都属此类。

  例如一个Modem类,即便其具有较多的特性,虚办法总数也很难跨越20个,而不同的Modem类完成,可以会改写其中大局部甚至全部虚办法。另一个例子是COM接口。由于COM组件思想基于接口,而一个粒度良好的接口一定是“肥大精干”的。比方IMalloc接口只需6个办法(不包括从IUnknown承袭来的3 个办法),IPersistFile共5个办法,通常用户本人写的COM接口中的办法数量也不跨越20。而在完成COM接口是,简直总是需求改写全部办法。这与低阶稀疏矩阵十分类似,因而值得用最复杂直接的查找表构造来完成——速度快,而且复杂直接。由于虚办法改写率高,查找表中的无效运用率较高。这种场景是C++多态性完成技术大大的用武之地,可以说C++特征的虚办法调用机制就是用来应对这种运用的。

  而第二种运用场景如出一辙,在这种场景中,对象比拟复杂,特性稀疏,行为变化无常,同一族系中兄弟对象数量庞大,而彼此之间迥然不同。此种对象中的虚办法数量多,而改写率低。GUI零碎和视频游戏是这种运用场景的典型代表。由于我们整天与Windows 零碎打交道,所以用Windows GUI零碎来阐明这种场景是最适合不过的了。我们晓得,在Windows图形界面上的简直一真实体从概念上讲都是Window对象,因而构成了一个对象族系。这个族系有三个突出的特点。一是行为多,特征多变(或许说虚办法数量多)。Microsoft Windows零碎直接定义了数百个窗口音讯,并允许用户运用WM_USER+n和WM_APP+n的方式定义新的音讯,用面向对象的话来说,就相当于给Windows零碎中的一切Window对象定义了数百个可供改写的虚办法,并且还允许用户自在扩展新的虚办法。

  第二个特点是改写率低,同族对象之间迥然不同。通常我们关于绝大少数的窗口音讯都是用DefWindowProc来分歧处置,或许用SendMessage函数将音讯转发(委托)给零碎提供的规范窗口对象处置,这也就是相当于把这些音讯交给基类窗口对象来处置,而只阻拦(改写)其中几个至几十个音讯(办法)。绝对于窗口对象族庞大的虚办法数量来说,改写率通常不跨越20%。第三个特点是同族兄弟类数量庞大。从规范窗口到异型窗口,从对话框到按钮,从工具条到文本框,一切的一切都是Window,甚至于两个按钮看上去完全一样,仅仅是caption不同,按下时执行的操作不同,就需求用不同的类来结构。因而在一个普通规模的运用顺序GUI界面零碎中,结构上百个迥然不同的窗口类是并不奇特的。任何一个对Win32 API有一定了解的开发者,对此都不难领会。

  从第1节关于C++虚办法调用机制的引见可以很容易地晓得,C++那种基于相对地位的、不带任何自描画信息的查找表构造,并不适用于上述的第二种场景。假定强行运用C++原生的对象模型来完成相似Windows的GUI零碎,那么后果是这样的:基类(无妨设为KWindow类)要定义1000个虚办法(其中应该留出多少地位供用户扩展之需呢?),从而拥有一个长达1000的查找表,而一切的直接和直接派生类对象,为了坚持与KWindow 在办法查找表构造上的兼容,都要至多容纳一个长达1000的查找表。

  我们举一个极端的例子来欣赏一下这种处置方案的荒唐性,假定有一个类KPushButton从KWindow中派生,并经过改写20个虚办法完成了一个规范的按钮控件,那么它的虚办法查找表中有多少项?对不起,不是20 项,而是至多1000项(假定它没有参与新的办法的话),其中绝大少数仅仅是KWindow虚办法表的原封不动的克隆,只需20项属于它本人,只需这20项真正有意义,办法表中980项被糜费掉了。它们独一的意义在于占据一些地位,使得“指针加偏移”的计算可以持续精确地寻址。你以为事情曾经很蹩脚了?不,理想上还可以更蹩脚!

  假定你需求一个规范按钮,它的外观、颜色、文字和其他行为都与KPushButton完全一样,仅仅是相应CLICK事情的操作不同,你需求怎样办?显然是从KPushButton中派生本人的KMyPush-ButtonOK类,然后改写其中的1 个办法(可以是叫做OnClick的)。那么在这个新的类中,虚办法表是多长呢?是1项吗?不是。是20项吗?也不是。理论上,是1000项!其中只需1项(OnClick)表现了它存在的意义,其他999项(在32位机器上占据3996个字节)简直完全被糜费掉了!一个中等规模的运用顺序中布置几十个界面,数百个自定制控件,则仅在虚办法表上糜费的存储空间即抵达数百KB甚至1MB以上。也许这个数字在明天用GB 大筐装主存的时代真实是小儿科,但是其面前所表现的思绪之美丽却是任何一个有点良知的开发者(尤其是C++开发者)所不能容忍的。

  也正是由于这个缘由,从OWL 到VCL,.. 从MFC到Qt,致使于近几年呈现的GUI和游戏开发框架,一切触及大批事情行为的C++ GUI Framework没有一家运用规范的C++多态技术来结构窗口类层次,而是各自为战,创造出五花八门的技术来绕过这个暗礁。其中比拟经典的处置方案有三,区分以VCL 的静态办法、MFC的全局事情查找表和Qt 的Signal/Slot为代表。而其面前的思想是分歧的,用Grady Booch的一句话来总结,就是:“当你发现零碎中需求大批类似的小型类的时分,该当用大批类似的小型对象处置之。”2 也就是说,将一些原本会招致需求派生新类来处置的效果,用实例化新的对象来处置。这种思绪简直一定招致相似C#中delegate那样的机制成为必需品。惋惜的是,规范C++ 不支持delegate。虽然C++社群里有很多人做了各种努力,运用了诸如template、functor等初级技巧,但是在效果上间隔真正的delegate还有差距。因而,为了坚持处置方案的复杂,Borland C++Builder扩展了__closure关键字,MFC创造出一大堆怪模怪样的宏,Qt搞了一个moc前处置器,八仙过海,各显神通。

  让我们小结一下,面向对象多态性有两种不同的运用场景,而C++的规范多态技术只适宜其中一种,关于另一种并不适宜,必需以其他机制完成。

  处置思绪和建议

  或许有读者读到这里,会对C++发作很大的狐疑。需求阐明的是,C++选择的多态性完成技术是完全契合C++哲学的。而且,C++允许你以各种可以的方法来处置这个效果。时至昔日,依托各种成熟的GUI框架,大少数状况下我们可以自动绕过暗礁。

  效果的严重性在于,由于C++教育上的效果,很多开发者关于C++原生多态技术在上述第二种运用场所中的局限性看法缺乏,因而当他们面临相似的效果时,会不盲目地踏入圈套中。在此我愿提示C++开发者,当你面对的零碎中含有规范的事情处置特征,而且事情数量较大时,请慎重思索你的类层次构造设计。可以思索模拟MFC或许Qt的处置办法,但在我看来,一个愈加直接而且复杂的办法是,模仿本文第1节中描画的、基于字符串比拟的办法查找表,用一个单一的音讯分发对象来向各个对象分发音讯。由于这个音讯分发对象会常常需求调整变化,将它独自放在一个DLL 甚至COM组件中,在运转时加载到进程内。这种方案不是最精巧的,但是在大少数状况下无效,并且完成起来比拟复杂。限于篇幅,这里不详细描画。

  理想上,我自己以为,C++言语该当从编译器上处置这个效果。根本思绪为,当基类虚办法数量大而派生类改写的办法数量小的时分(这个信息可以从编译进程中失掉),改动派生类对象的虚办法查找机制,改按地位查找为按被调用函数理论信息查找。这样一来,派生类中的虚办法表可不用与基类坚持构造上的分歧,从而防止了空间上的糜费。这种思绪跟Delphi/Object Pascal言语中dynamic关键字有类似之处。本文不再赘述。

«上一页12下一页»


相关评论