转至繁体中文版     | 网站首页 | 图文教程 | 资源下载 | 站长博客 | 图片素材 | 武汉seo | 武汉网站优化 | 
最新公告:     敏韬网|教学资源学习资料永久免费分享站!  [mintao  2008年9月2日]        
您现在的位置: 学习笔记 >> 图文教程 >> 软件开发 >> Delphi程序 >> 正文
Delphi对象模型(Part VI)         ★★★★

Delphi对象模型(Part VI)

作者:闵涛 文章来源:闵涛的学习笔记 点击数:1494 更新时间:2009/4/23 18:40:53

 

Delphi对象模型 (PART VI)

 

Delphi对于面向对象编程的支持丰富而且强大。除了传统的类和对象,Delphi还提供了接口,异常处理,多线程编程等特性。这一章节深入讲解了Delphi的对象模型。读者应当对标准的Pascal比较熟悉,并且对有关面向对象编程的基本法则有一定了解。

(本文的英文原文将Delphi与Object Pascal统一表述为Delphi,可能有概念不清之嫌疑。但在大多数情况下,相信读者能够根据上下文来判定文中所述之Delphi的具体含义——译者注。)

Interfaces接口

接口定义了包含一组抽象方法的类型。一个类,即使是自一个简单的基类继承而来也可以实现任意多的借口。接口与抽象类有些相似(即没有任何字段并且所有方法都是抽象方法的类),并且Delphi提供了附加的功能。Delphi的接口有时很象COM(组件对象模型)借口,但是使用Delphi的接口并不需要你了解有关COM的内容,同时你还可以将接口用作其他许多用途。

你可以声明一个新的接口——它继承于一个已经存在的接口。接口的声明包含了方法和属性的声明,但是没有字段。正如所有的类都继承于TObject一样,所有的接口类继承自IUnknown。接口IUnknown定义了三个方法:_AddRef,_Release,以及QueryInterface。如果你对COM熟悉的话,对此三个方法便不会陌生。前两个方法用于管理实现此接口的对象的生命周期引用计数。第三个方法用于存取对象可能实现的其他接口。

当你想要声明一个实现了一个或者多个接口的类时,你必须实现接口中所声明的所有方法。新的类可以直接实现接口的方法,也可以将此实现委托给一个属性——其值为一个接口。实现_AddRef,_Release以及QueryInterface方法最简单的方法就是继承TInterfacedObject及其派生类的方法,当然你也可以继承自其他类如果你想自己定以方法的实现的话。

新类在实现接口的方法时必须使用于接口方法一致的方法名,参数以及调用约定。Delphi自动将类的方法与接口的相应方法配对。假如要使用不同的方法名,你可以使用不同的方法名来重定向接口的方法。用作重定向的方法必须具有于接口的方法一致的参数和调用约定。这一特性非常重要,当一个类需要实现多个接口,而其中有重复的方法名时尤其如此。请在第五章查找关键字Class,以获得有关重定向方法的更多内容。

类可以使用implements指示符将接口的实现委托给一个属性。该属性的值必须得是该类将要实现的接口类型。当对象被映射到该接口上时,Delphi自动获取该属性的值,并且返回该接口。参考第五章中关于implements指示符的内容。

对于每个非委托方式实现的接口,编译器为其创建一个隐含的字段用于存放指向该接口的VMT。接口的字段正好位于对象隐含的VMT字段之后。正如对象引用其实是指向对象的隐含的VMT字段的指针,接口的引用也是指向隐含的VMT字段的一个指针。当对象被创建时Delphi自动初始化隐含字段。参考第三章有关编译器如何使用RTTI来追踪VMT和隐含字段的内容。

Reference counting引用计数

编译器触发对_AddRef和_Release的调用以管理接口对象的生命周期。要使用Delphid的自动的引用计数,声明一个接口类型的变量即可。当你将一个接口引用赋值给一个接口变量时,Delphi自动调用_AddRef。当改变量离开作用域时,Delphi自动调用_Release。

_AddRef和_Release的行为完全取决于你。如果你从TInterfacedObject继承,则这些方法完成引用计数的功能。_AddRef方法用于增加引用计数,_Release用于将引用计数减一。当引用计数为0时,_Release方法将释放对象。如果你从其他类继承而来,则你可以定义自己的方法。但是,你应当正确的实现QueryInterface方法,因为Delphi正是基于此来实现As操作。

Typecasting类型转换

Delphi调用QueryInterface来对接口实现部分as操作的功能。你可以使用as操作符将一个接口转换为另外一个接口。Delphi调用QueryInterface以获得一个新的接口引用。如果QueryInterface返回一个错误,则as操作将触发一个运行期错误。(在SysUtils单元中该运行其错误被映射到EIntfCastError异常类中。)

你可以用自己的方式来实现QueryInterface方法,虽然可能你更倾向于与TInterfacedObject的实现接近的那种。例子 2-13 显示的是一个类实现了普通的QueryInterface方法,但是对于_AddRef和_Release方法的实现确大不相同。稍后你将看到这样做有什么用处。

例 2-13:无需引用计数的接口类

type
  TNoRefCount = class(TObject, IUnknown)
  protected
    function QueryInterface(const IID:TGUID; out Obj):HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;
 
function TNoRefCount.QueryInterface(const IID:TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := Windows.E_NoInterface;
end;
 
function TNoRefCount._AddRef: Integer;
begin
  Result := -1
end;
 
function TNoRefCount._Release: Integer;
begin
  Result := -1
end;
 

Interfaces and object-oriented programming接口和面向对象编程

接口最重要的作用就是将类型继承(type inheritance)与类继承(class inheritance)分开。类继承是代码重用的一项有效的工具。派生的类轻松的继承了基类的字段,方法以及属性,并且不需要重新实现公用的方法。在一个强类型的语言中,比如Delphi中,编译器将一个类看作是一种类型,因此类继承与类型继承的概念似乎有些重叠了。但是尽可能地,我们对于类型(type)和类(Class)还是应当严格区分。

许多有关面向对象编程的书籍都将继承关系描述为“是”的关系,比如,一个TSavingsAccount“是”TAccount。你可以体会到相同的含义,当你使用Delphi的Is操作符,来测试一个Account变量是否是TSavingsAccount。

上文例子中的简单的“是”关系已经不能适应要求。正方形属于矩形的一种,但这并不意味着你愿意将TSquare继承自TRectangle。矩形属于多边形的一种,但你可能不希望TRectangle继承自TPolygon。类继承强制派生的类保存基类中声明的所有字段,但这种情况下,派生类并不需要这些信息。一个TSquare对象只需保存它所有边的一个单一长度。然而,一个TRectangle对象却必须保存两个长度。一个TPolygon对象则需要保存许多条边和顶点位置。

解决的方案就是将其从类继承(类C继承了B的字段和方法,而B则继承了A的字段和方法)分离为类型继承(正方形是矩形,矩形又是多边形)。使用接口实现类型继承,则你可以让类继承做它擅长的:字段和方法的继承。

换句话说就是,ISquare继承自IRectangle,而后者又继承自IPolygon。接口遵从了“是”的关系。完全的与接口分离,类TSquare实现了接口ISquare和IRectangle和IPolygon。TRectangle实现了IRectangle和IPolygon。

提示:
 COM编程的一个约定是将接口的名称命名为I打头的。Delphi的所有接口都遵循了这个约定。注意这只是一个有用的约定,并不是语言的强制要求。

从实现上而言,你可以声明符加的类以达到代码重用的目的。比如,使用TBaseShape实现对所有形状的公用字段和方法。TRectangle继承自TBaseShape然后实现跟根据矩形的特点实现相应方法。多边形依然继承自TBaseShape,并且根据多边形的特点实现相应的方法。

一个画图程序可以操作IPolygon接口来使用各种形状。例子 2-14显示的是基于这种设想的简单的类和接口。注意到每个接口都同时声明了GUID(全局唯一标识符)。使用QueryInterface时GUID是必须的。如果要使用接口的GUID,你可以直接使用接口的名称。Delphi会自动将接口的名称转换为对应的GUID。

例 2-14:分离类型和类继承

type
  IShape = interface
  [''''{50F6D851-F4EB-11D2-88AC-00104BCAC44B}'''']
    procedure Draw(Canvas: TCanvas);
    function GetPosition: TPoint;
    procedure SetPosition(Value: TPoint);
    property Position: TPoint read GetPosition write SetPosition;
  end;
 
  IPolygon = interface(IShape)
  [''''{50F6D852-F4EB-11D2-88AC-00104BCAC44B}'''']
    function NumVertices: Integer;
    function NumSides: Integer;
    function SideLength(Index: Integer): Integer;
    function Vertex(Index: Integer): TPoint;
  end;
  IRectangle = interface(IPolygon)
  [''''{50F6D853-F4EB-11D2-88AC-00104BCAC44B}'''']
  end;
  ISquare = interface(IRectangle)
  [''''{50F6D854-F4EB-11D2-88AC-00104BCAC44B}'''']
    function Side: Integer;

[1] [2]  下一页


[VB.NET程序]VB.Net中文教程(13)   Whole-Part关系  [Delphi程序]The Delphi Object Model (PART III)
[Delphi程序]The Delphi Object Model (PART II)  [Delphi程序]The Delphi Object Model (PART I)
[Delphi程序]Delphi对象模型(Part III)  [Delphi程序]Delphi对象模型(Part II)
[Delphi程序]Delphi对象模型(Part I)  [Delphi程序]Delphi对象模型(Part V)
[Delphi程序]Delphi对象模型(Part IV)  [Delphi程序]防止全局hook入侵Delphi版,2000以上系统适用(pa…
教程录入:mintao    责任编辑:mintao 
  • 上一篇教程:

  • 下一篇教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)

    同类栏目
    · C语言系列  · VB.NET程序
    · JAVA开发  · Delphi程序
    · 脚本语言
    更多内容
    热门推荐 更多内容
  • 没有教程
  • 赞助链接
    更多内容
    闵涛博文 更多关于武汉SEO的内容
    500 - 内部服务器错误。

    500 - 内部服务器错误。

    您查找的资源存在问题,因而无法显示。

    | 设为首页 |加入收藏 | 联系站长 | 友情链接 | 版权申明 | 广告服务
    MinTao学以致用网

    Copyright @ 2007-2012 敏韬网(敏而好学,文韬武略--MinTao.Net)(学习笔记) Inc All Rights Reserved.
    闵涛 投放广告、内容合作请Q我! E_mail:admin@mintao.net(欢迎提供学习资源)

    站长:MinTao ICP备案号:鄂ICP备11006601号-18

    闵涛站盟:医药大全-武穴网A打造BCD……
    咸宁网络警察报警平台