法(Virtual Method)。而在TNewClass中,One方法后面多了一个Override关键字,表示该方法进行了重载(Override)。重载技术能够实现许多特殊的功能。
让我们来仔细分析它们的实现部分。在TMyclass.One方法的实现部分,调用ShowMessage过程弹出一个对话框,说明该方法已被调用;这里没有任何特别的地方。在TNewClass.One方法中,出现了一条以前从未出现过的语句:
Inherited;
这个词的中文意思是“继承”。我们暂时不要去涉及到太过复杂的OOP概念,只要知道这条语句的功能就是了。它的功能是调用基类中相当的虚拟方法中的代码。例如,你如果使用以下代码:
var
AObject: TNewClass;
begin
AObject := TNewClass.create;
AObject.One;
AObject.free;
end;
那么程序将弹出两次对话框,第一次是调用TMyclass类中的One方法,第二次才是TNewClass.One方法中的代码。
重载技术使得我们不但可以在派生类中添加基类没有的数据和方法,而且可以非常方便地继承基类中原有方法的代码,只需要简单地加入Inherited就可以了。如果你不加入Inherited语句,那么基类的相应方法将被新的方法覆盖掉。但是必须注意,重载只有在基类的方法被标志为Virtual时才能进行,而且重载的方法必须具有和虚拟方法完全相同的参数类型。
虚拟方法还有一种特例,即抽象方法:
procedure One;override;abstract;
在One方法后面,不但有override关键字,还多了一个abstract关键字(意为抽象)。这种方法称为抽象方法(在C++中称为纯虚拟函数)。含有抽象方法的类称为抽象类。抽象方法的独特之处在于,它只有声明,而根本没有实现部分,如果你企图调用一个对象的抽象方法,你将得到一个异常。只有当这个类的派生类重载并实现了该方法之后,它才能够被调用。(在C++中,甚至根本就不能建立一个抽象类的实例。)
既然如此,那么这种抽象方法又有什么用呢?这个问题我们将在接下来的“多态”部分进行讨论。
1.3 多态
多态相对来说比较复杂一点。不过不要担心,它的内容比较少,而且如果以前的知识掌握得比较稳固的话,多态的概念是水到渠成的。
先来讨论一下类型的兼容性问题。下面是一个例子:
type
TAnimal = Class
Procedure Voice;virtual;
...
end;
TDog = Class(TAnimal)
Procedure Voice;Override;
...
end;
implementation
Procedure TAnimal.Voice;virtual;
Begin
PlaySound('Anim.wav',0,snd_Async);
End;
Procedure TDog.Voice;virtual;
Begin
PlaySound('Dog.wav',0,snd_Async);
End;
TDog类继承了TAnimal类,并重载了其中的Voice方法。PlaySound是一个WIN API函数,可以播放指定的wav文件。(这个函数的定义在MMSystem.pas文件中可以找到。)
先看这段代码:
var
MyAnimal1, MyAnimal2: TAnimal;
Begin
MyAnimal1 := TAnimal.Create;
MyAnimal2 := TDog.Create;
...
在实现部分的第一行中,建立了一个TAnimal类型的对象,并将其赋予TAnimal类型的变量MyAnimal1。这是很正常的事。但在第二行中,建立了一个TDog类型的对象,并将其赋予了TAnimal类型的变量MyAnimal2。这看上去令人吃惊,但这些代码是完全合法的。
众所周知,Pascal以及Object Pascal是一种类型定义严格的语言,你不能将某个类型的值赋予不同类型的变量,例如将一个整型值赋予布尔型变量,将会导致出错。但是,这个规则在涉及到OOP领域时,出现了一个重要的例外,那就是:可以将一个子类的值赋予一个父类类型的变量。但倒过来却是不行的,一个父类的值决不能赋予一个子类类型的变量。
如果将这个原则放到现实世界中,那就很容易理解了:“狗”继承自“动物”,因为狗也是一种动物。所以可以将一个“狗”类型的值赋予“动物”类型的变量,因为“狗”具有“动物”的一切特征。但反过来,“动物”不具有“狗”的所有特征,因此反向赋值是不行的。
那么,这种兼容规则在编程中究竟有什么用处呢?
请注意下面这段代码:
var
MyAnimal1, MyAnimal2: TAnimal;
Begin
MyAnimal1 := TAnimal.Create;
MyAnimal2 := TDog.Create;
MyAnimal1.Sound;
MyAnimal2.Sound;
...
MyAnimal1和MyAnimal2都是TAnimal的变量,而且都调用了Sound方法。但是,执行的结果是完全不同的:前者执行的是TAnimal.Voice的代码,而后者执行的是TDog.Voice的代码!其原因很简单,因为MyAnimal1被赋予了TAnimal类型的对象,而MyAnimal2被赋予了TDog类型的对象。也就是说,一个TAnimal类型的变量,当它调用Sound方法时,所执行的代码是不确定的:可能执行TAnimal.Voice的代码,也可能执行的是TDog.Voice的代码,取决于它当时引用的是一个什么样的对象。
再看:
MyAnimal1 := TAnimal.Create;
MyAnimal1.Sound;
MyAnimal1.free;
MyAnimal1 := TDog.Create;
MyAnimal1.Sound;
...
同一个变量MyAnimal1,在第一次调用Sound方法时,执行的是TAnimal.Voice的代码,在第二次时执行的是TDog.Voice的代码。MyAnimal1.Sound这行代码不需要变化,程序可以根据不同的情况赋予该变量不同的对象,从而使它执行不同的代码。这就是多态的定义。
这个非常重要的特点大大地增加了代码的可复用性。如前所述,只需要简单地写下一行代码,就可以让程序执行不同的功能,因为这个虚拟方法同TAnimal的任何派生类都是兼容的,甚至连那些还没有编写出来的类也是一样。而程序员并不需要了解这些派生类的细节。利用多态性写出来代码,还具有简洁和维护性好的特点。
现在我们可以回到本文的1.2节结尾处的问题了。抽象方法本身不能够做任何事情,必须在子类中被重载并实现,才能够完成有意义的工作。但抽象方法的存在,相当于为父类留下了一个接口,当程序将一个子类的对象赋予父类的变量时,父类的变量就可以调用这个方法,当然此时它运行的是相应的子类中重载该方法的代码。如果没有这个抽象方法,父类的变量就不能调用它,因为它不能调用一个只在子类中存在、而在父类中不存在的方法!
关于OOP的介绍就到此这止。在以上这些篇幅里,介绍的只是OOP最基本的一些概念,让读者对OOP有一定的系统认识,也为
上一页 [1] [2] [3] [4] [5] [6] [7] [8] 下一页