unit Unit2;
interface
uses
Classes,Dialogs;
type
TA = class
public
a:string;
procedure Fuc; end;
TB =class(TA)
public
a:string;
procedure Fuc;
end;
implementation
{ TA }
procedure TA.Fuc;
begin
ShowMessage('a');
end;{ TB }
procedure TB.Fuc;
begin
ShowMessage('b');
inherited;
end;
end.
首先,这样的代码能编译通过,运行。TB中的Fuc覆盖了TA中的。
字段也能重名。这两点我真的很不明白,请知道的人给与解释。
还有上面的代码我觉得已经否定了virtual关键字的作用,因为没有这个关键字一样实现重写。
virtual与dynamic的本质区别是什么?动态和虚到底有什么区别?
interface
uses
Classes,Dialogs;
type
TA = class
public
a:string;
procedure Fuc; end;
TB =class(TA)
public
a:string;
procedure Fuc;
end;
implementation
{ TA }
procedure TA.Fuc;
begin
ShowMessage('a');
end;{ TB }
procedure TB.Fuc;
begin
ShowMessage('b');
inherited;
end;
end.
首先,这样的代码能编译通过,运行。TB中的Fuc覆盖了TA中的。
字段也能重名。这两点我真的很不明白,请知道的人给与解释。
还有上面的代码我觉得已经否定了virtual关键字的作用,因为没有这个关键字一样实现重写。
virtual与dynamic的本质区别是什么?动态和虚到底有什么区别?
a1, a2 : TA;
begin
a1 := TA.Create;
a2 := TB.Create;
a1.Fuc;
a2.Fuc;
a1.Free;
a2.Free;
end;你试试加上virtual/dynamic、override和不加的运行结果有什么差别dynamic和virtual的区别主要是,前者用时间换空间,后者用空间换时间
调用virtual函数的时候,首先找到类类型rtti中的虚函数列表,然后call该虚函数对应的指针。也就是说,每一个有虚方法的类的继承类,它的虚方法列表都不小于基类,哪怕没有override,也会把基类的虚方法地址放到虚方法列表中
而dynamic则需要通过System._CallDynaInst调用。CallDynaInst调用GetDynaMethod寻找动态方法的地址,动态方法放在类类型rtti的vmtDynamicTable中。它的寻找过程比较复杂,首先在调用的时候会传入一个动态方法的索引,然后找到Dynamic Table,在动态列表中寻找是否存在该索引,如果存在的话通过索引找到方法指针并返回;如果不存在的话,则继续跳到父类的动态列表重复上面的过程,直到找到为止。如果一个类没有继承基类的动态方法,那么它的动态方法列表是空的,这样减少了继承类的类型信息的大小
var
b: TB;
begin
b := TB.Create;
try
b.Fuc;
finally
b.Free;
end;
end;// 针对(抽象)基类操作
var
a: TA;
begin
a := TB.Create;
try
a.Fuc;
finally
a.Free;
end;
end;像第一段代码是直接针对具体类(TB)来操作,此时会直接调用TB的Fuc,和TA的Fuc及声明没有关系。
而第二段代码则不同,客户想针对抽象基类操作,由于TA.Fuc没有定义为virtual/dynamic,所以调用的仍是TA.Fuc,而不是TB.Fuc。(这是客户想要的吗?)那我们为什么要针对抽象编程?主要是通过*继承*和*多态*(即将基类方法定义为virtual/dynamic,子类再覆盖此方法),让同一个动作在各个子类中可以有不同的行为/实现。(而传统的面向过程的方式是直接在一个函数中用if/case做判断,通过修改而不是继承的方法来实现扩展。)比如: TAnimal = class
public
procedure SayHello; virtual;
end; TDuck = class(TAnimal)
procedure SayHello; override;
end; TBird = class(TAnimal)
procedure SayHello; override;
end; 客户的调用代码: TClient = class
private
fAnimal: TAnimal;
public
constructor Create(animal: TAnimal); // 假设通过构造函数的参数传入animal
procedure Test;
end; procedure TClient.Test;
begin
fAnimal.SayHello;
end; 如果传入的annimal是TDuck的实例,则调用的即是TDuck.SayHello;如果是TBird的示例,则调用TBird.SayHello。
P.S. 关于virtual和dynamic,其实语义上都是一样的,子类都能通过覆盖(override)来实现多态,区别在于内存布局不同而已。
如果定义为virtual,编译器会在所有子类的VMT中生成所有的虚拟方法列表(无论该子类中是否override过);
如果定义为dynamic,编译器仅仅在子类的DMT中生成override过的虚拟方法(这样对于没有override就需要遍历父类来找到虚拟方法的地址)。这也就是Seamour说的用空间换时间(virtual)以及用时间换空间(dynamic)。