今天看了才真正的明白什么是多态性,其实就是覆盖。基类中有一个方法,必须是虚方法,在其子类中用override进行覆盖。
字串6
在静态方法中,定义一个和基类一样的方法,一个变量为基类类型,子类对象对其赋值,方法为基类方法,只有变量为子类类型方法才是子类方法,但用override覆盖后,对象是什么类就用什么类的方法。 字串4
摘录了一篇文章 字串3
多态的概念与接口重用 字串1
首先,什么是多态(Polymorphisn)?按字面的意思来讲,就是“多种形状”。笔者也
没有找到对多态的非常学术性的描述,暂且引用一下Charlie Calvert对多态的描述——多态
字串6
性是允许用户将父对象设置成为与一个或更多的它的子对象相等的技术,赋值之后,基类 字串9
对象就可以根据当前赋值给它的派生类对象的特性以不同的方式运作。 字串6
更简单地说就是:多态性允许用户将派生类类型的指针赋值给基类类型的指针。多态
字串1
性在Object Pascal中是通过虚方法(Virtual Method)实现的。 字串6
什么是“虚方法”?虚方法就是允许被其派生类重新定义的方法。派生类重新定义基
字串9
类虚方法的做法,称为“覆盖”(override)。
字串5
这里有一个初学者经常混淆的概念:覆盖(override)和重载(overload)。如前所述, 字串6
覆盖是指派生类重新定义基类的虚方法的方法。而重载,是指允许存在多个同名函数,这
字串7
些函数的参数表不同(或许是参数个数不同,或许是参数类型不同,或许两者都不同)。 字串8
重载的概念并不属于“面向对象编程”。重载的可能的实现是:编译器根据函数不同的参
数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器 字串4
来说)。例如,有两个重载的同名函数
字串6
function func(p : integer) : integer; overload; 字串2
function func(p : string) : integer; overload; 字串7
那么编译器做过修饰后的函数名可能是:int_func、str_func。如果调用
func(2); 字串4
func(′hello′); 字串5
那么编译器会把这两行代码分别转换成: 字串5
int_func(2); 字串9
str_func(′hello′);
字串6
这两个函数的调用入口地址在编译期间就已经静态(记住:是静态!)确定了。这样 字串7
的确定函数调用入口地址的方法称为早绑定。 字串3
而覆盖则是:当派生类重定义了基类的虚方法后,由于重定义的派生类的方法地址无 字串8
法给出,其调用地址在编译期间便无法确定,故基类指针必须根据赋给它的不同的派生类 字串6
指针,在运行期动态地(记住:是动态!)调用属于派生类的虚方法。这样的确定函数调
字串7
用地址的方法称为晚绑定。引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,
它就不是多态”。 字串2
..注意:重载只是一种语言特性,与多态无关,与面向对象也无关!
字串3
多态是通过虚方法实现的,而虚方法是通过晚绑定(或动态绑定)实现的。
字串4
其次,多态的作用是什么呢?前两节已经讲到,封装可以隐藏实现细节,使得代码模
字串7
块化;继承可以扩展已存在的代码模块,它们的目的都是为了代码重用。而多态则是为了
字串4
实现另一个目的——接口重用。
什么是接口重用?举一个简单的例子,假设有一个描述飞机的基类:
字串6
字串2
type 字串2
TPlane = class
字串8
protected 字串1
FModal : String; // 型号
字串6
public 字串2
procedure fly(); virtual; abstract; // 起飞抽象方法 字串1
procedure land(); virtual; abstract; // 着陆抽象方法
function modal() : string; virtual; // 查寻型号虚方法 字串3
…… // 其他可能的操作 字串9
end; 字串5
然后,从TPlane派生出两个派生类,直升机(TCopter)和喷气式飞机(TJet): 字串4
TCopter = class(TPlane)
public
字串8
constructor Create(); 字串5
destructor Destroy(); override;
procedure fly(); override; 字串4
procedure land(); override; 字串3
function modol() : string; override; 字串4
…… //其他可能的操作
字串8
end;
字串7
TJet = class(TPlane)
public
字串1
constructor Create();
destructor Destroy(); override; 字串6
procedure fly(); override;
procedure land(); override;
字串8
…… //其他可能的操作,没有覆盖modal() 字串4
end; 字串8
TPlane类的声明中,fly和land方法都是被声明为virtual和abstract的,这是向编译器 字串6
指出这些方法是抽象(纯虚)的,也就是在TPlane类中不提供这些方法的实现,而派生类 字串7
则必须实现它,即规定了一套接口。凡是含有abstract方法的类被称为“抽象类”,永远无 字串6
法创建抽象类的实例对象。抽象类是被用来作为接口的。
现在,假设要完成一个机场管理系统,在有了以上的TPlane之后,再编写一个全局的 字串7
函数g_FlyPlane(),就可以让所有传递给它的飞机起飞: 字串8
procedure g_FlyPlane(const Plane : TPlane);
begin 字串8
Plane.fly(); 字串7
end;
字串3
是的,仅仅如此就可以让所有传给它的飞机(TPlane的派生类对象)正常起飞!不管 字串2
是直升机还是喷气式飞机,甚至是现在还不存在的、以后会增加的飞碟。这是因为,每个
派生类(真正的飞机)都可以通过“override”来定义适合自己的起飞方式。
字串8
可以看到,g_FlyPlane()函数接受的参数是TPlane类对象的引用,而实际传递给它的都
是 TPlane的派生类对象。现在回想一下本节开头所描述的“多态”:多态性是允许将父对
象设置成为与一个或更多的它的子对象相等的技术,赋值之后,父对象就可以根据当前赋
字串1
值给它的子对象的特性以不同的方式运作。很显然,
字串5
parent := child; 字串8
就是多态的实质!这里的飞机类(TPlane)作为一种接口,而该接口就是被重用的目标。 字串1
多态的本质就是“将派生类类型的指针赋值给基类类型的指针”(在Object Pascal中
![我要研发网[www.51dev.com]](/templets/images/toplogo.gif)
