基于栈的对象
C++的对象是基于栈的,你可以像普通数据类型一样声明它,访问它的成员,调用它的方法,然后由编译器自动清理栈现场,C++的对象更像一个增强的结构。
而后来的许多面向对象语言如Java,C#等都采用了基于堆的对象机制。Object Pascal呢?毫无疑问它的对象也是基于堆的,当我们看到对象与指针进行转换的代码时,觉得很正常,因为OP对象本质上就是一个指针。
那么Delphi只有基于堆的对象吗?它会不会存在一种基于栈的对象呢?回答这个问题之前先来看看下面的代码:
var
People: TPeople;
begin
People.Name := 'Tom';
People.SayMyName;
end;
大多数人会认为这是错的,因为对象还没有创建,而实际上代码执行的结果是正确的,它弹出一个消息框,说自己叫TOM。
现在来揭开TPeople这个类的真面目:
type
TPeople = object
private
FName: string;
public
procedure SayMyName;
property Name: string read FName write Fname;
end;
{ TPeople }
procedure TPeople.SayMyName;
begin
ShowMessage('my name is: ' + FName);
end;
这个类声明为Object,这是一种向后兼容的类声明方法,现在几乎没有人使用这个关键字来声明类了。但它却表明了Delphi存在的另一种对象机制:基于栈的对象。
Object类的对象的确是基于栈的,从上面的代码可以看出,它不需要创建和消毁,你可以像一个记录一样使用它,只不过这个“记录”是可以带方法的。
与其他类型的指针一样,Object类的对象指针就是PPeople = ^TPeople,要创建一个对象指针,使用New函数,但使用方法稍有不同,请看下面的代码:
type
PPeople = ^TPeople;
TPeople = object
private
FName: string;
public
constructor Create;
destructor Destroy;
procedure SayMyName;
property Name: string read FName write FName;
end;
{ TPeople }
constructor TPeople.Create;
begin
ShowMessage('I am born');
end;
destructor TPeople.Destroy;
begin
ShowMessage('I am die');
end;
procedure TPeople.SayMyName;
begin
ShowMessage('my name is: ' + FName);
end;
procedure TForm1.btn1Click(Sender: TObject);
var
People: PPeople;
begin
New(People, Create);
People^.Name := 'Tom';
People^.SayMyName;
Dispose(People, Destroy);
end;
上面的使用方法就和平常所用的Class十分相似了,不过我倒觉得Object对象和C++的Class更像一些,只是在特性上弱了一些。
有几点要特别指出的:
1. Object类并不继承自TObject,像上面代码那样声明一个类,表明这个类没有父类。
2. 声明Object类的实例时,不会像C++一样调用构造函数;可以用对象指针来代替,如上面的代码。
3. Object类不能声明published成员,这表示它的类型信息非常弱,实际上它的类型信息是tkRecord,而tkRecord除了有个名字外,没有办法获得其他有用信息。
就像Delphi帮助里面说的,Object类只是出于兼容的考虑,并不推荐使用这种类型的类。尽管如此,我认为在某些场合还是可以考虑使用,因为它是基于栈的,因此在存取速度上必定要比基于堆的快很多,一些简单的对象可以考虑用这种类型。
另外,KOL使用Object类建立起了它自己体系结构,使得编译出来的Exe尺寸大幅减小。