cppProgramer:=TCppProgramer(TDocManager.Create); cppProgramer.startWorking; {调用的竟然是TDocManager.startWorking, 这就是用派生类指针/引用指向基类对象实现的多态。这种方法极不安全, 而且没有什么必要} cppProgramer.Free; end; 试一试 为获得这种多态的感性认识,建议动手试试,上面说到这种使用方法会有潜在的不安全性(如访问异常),而上面的程序运行一点错误都没有出现,想想为什么?什么情况下会出现访问异常,动手写个访问异常的例子,你将收获更多。(参考Demo程序) 2 VCL中多态的应用 2.1构造与析构方法 构造方法的多态 由于构造方法可以看作“特殊的”类方法,在Tcomponent之后的所有的派生类的又被重新定义为虚类方法,因此要实现构造方法的多态性,就得使用类引用,在Delphi中有个经典的例子,就在每一个工程文件中都有一个类似下面的代码: Application.CreateForm(TForm1, Form1); 其方法的定义: procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference); var// InstanceClass为类引用。 Instance: TComponent; begin Instance := TComponent(InstanceClass.NewInstance); {NewInstance方法的声明:class function NewInstance: TObject; virtual; (system单元 432行)是一个类方法,同时也是虚方法,我们把它称之为虚类方法。InstanceClass是一个类引用,实现了类一级的多态,从而实现了创建组件的接口重用} TComponent(Reference) := Instance; try Instance.Create(Self);//调用构造方法,进行初始化 except TComponent(Reference):= nil;//消除“野“指针!good raise; end; {如果创建的是窗口且还没有主窗体的话,就把刚创建的窗体设为主窗体} if (FMainForm = nil) and (Instance is TForm) then begin TForm(Instance).HandleNeeded; FMainForm := TForm(Instance);//设置主窗体 { 实际上,在项目选项(project->options)中设置主窗体,实际上就把工程文件中相应的窗体语句提到所有创建窗体语句之前。} end; end; 2) 析构方法的多态请参考《剖析Delphi中的构造和析构》,第3.3节 2.2 Tstrings 字符串数组处理在Delphi的控件中十分常见,通常是一些Items属性,我们用起来也特别地方便(因为都是一样的使用接口),这得益于Delphi中字符串数组的架构的设计。这是一个成功的设计。 由于很多控件中要用到字符串数组,如ComboBox,TstringGrid等等,但每个控件中的字符串数组又不同,Delphi由此把字符串数组但抽象出来,从而出现了很多与之相关的类。其中基类Tstrings只是提供为各种调用提供接口,具体实现完全可由其派生类中实现,因此,把Tstrings定义为抽象类。 下面就来看看基类TStrings类的常用方法的定义(参见Classes单元第442行): TStrings = class(TPersistent) protected ... function Get(Index: Integer): string; virtual; abstract; procedure Put(Index: Integer; const S: string); virtual; function GetCount: Integer; virtual; abstract; … public function Add(const S: string): Integer; virtual; //实际调用的是Insert {添加一字符串S到字符串列表末尾} procedure AddStrings(Strings: TStrings); virtual; {添加字符串列表Strings到该字符串列表末尾} procedure Insert(Index: Integer; const S: string); virtual; abstract; {抽象方法,在第Index位置插入一新字符串S} procedure Clear; virtual; abstract; {清除所有的字符串} procedure Delete(Index: Integer); virtual; abstract; {删除某个位置上的字符串} function IndexOf(const S: string): Integer; virtual; {获取S在字符串列表中的位置} function IndexOfName(const Name: string): Integer; virtual; {Returns the position of the first string with the form Name=Value with the specified name part} function IndexOfObject(AObject: TObject): Integer; virtual; {获取对象名为AObject:的对象在字符串列表中的位置} procedure LoadFromFile(const FileName: string); virtual; {Fills the list with the lines of text in a specified file} procedure LoadFromStream(Stream: TStream); virtual; {Fills the list with lines of text read from a stream} procedure SaveToStream(Stream: TStream); virtual; {Writes the value of the Text property to a stream object} property Strings[Index: Integer]: string read Get write Put; default; {References the strings in the list by their positions} property Values[const Name: string]: string read GetValue write SetValue; {Represents the value part of a string associated with a given Name, on strings with the form Name=Value.} … end; 从Tstrings的定义可以看出,它的大部分Protected和Public的方法都是虚方法或是抽象方法。(请Soul来补充一些,TstringList->TstringGridString) 2.3其他(请soul来补充) 如果你对多态还不明白的话,那请你记住多态的实质: “相同的表达式,不同的操作”(就这么简单) 从OOP语言的实现来讲,多态就是使用基类的指针/引用来操作(派生类)对象,在运行期根据实际的对象,来执行不同的操作方法;或者换一种更形象的说法:由对象自己来决定自己操作方式,编译器只需下达做什么的命令(做什么what),而不要管怎么做(how),"怎么做"由为对象自己负责。这样就实现了接口和实现的分离,使接口重用变得可能。 其实多态也简单!那么使用多态应该注意什么呢?下面我的两点几点建议: 分析业务逻辑,然后把相关的事物抽象为“对象”,再用对象方法封装业务逻辑。把一些具有多态性的操作,在基类中声明为虚方法(virtual Method),对于在基类没有必要实现的就声明为抽象方法(virtual Abstract Method),然后在其派生类中再覆载它(Override),在使用的时候用基类的引用/指针来调用,这样顺理成章地实现了现实世界中的多态性。记住千万不要为了多态,而去实现多态,这是一种走形式化的做法,是没有意义的。 由于基类与派生类有一种天然“耦合”关系,修改基类就会导致“牵一发而动全身”,这将是非常麻烦的事情!因此要尽量弱化基类的功能实现,必要时把它设计为“抽象类”,并保证稳定的接口,这可以通过预留一些冗余的虚函数(或抽象函数)来实现。 相关问题 讨论Delphi的多态: http://www.delphibbs.com/delphibbs/dispq.asp?lid=1753965 关于多态性: http://www.delphibbs.com/delphibbs/dispq.asp?lid=1854895 什么是多态?在日常编程中有哪些运用?http://www.delphibbs.com/delphibbs/dispq.asp?lid=960465 overload 与 override有何区别,请执教?http://www.delphibbs.com/delphibbs/dispq.asp?lid=296739 派生类的指针指向基类对象的问题 http://www.delphibbs.com/delphibbs/dispq.asp?lid=2104106 (最后一个问题是我在深入学习多态时在DelphiBBS上提的,曾引起热烈的讨论,建议看看)