Delphi对象模型(Part III)

原创 2001年09月05日 21:08:00

 

Delphi对象模型 (PART III) <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

 

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

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

 

构造器(Constructor)

每一个类都有一个或多个可能是自基类继承而来的构造器。按照惯例,构造器通常命名为Create,但你也可以使用其他名称。有些构造器以Create打头,为了传递更多的信息,被命名为诸如CreateFromFile或者CreateFromStream这样的名字。通常情况下,使用”Create” 这个名字就可以了,因为你可以使用重载来定义多个相同名字的构造器。另一个原因是为了保持与C++Builder的兼容。因为C++不允许构造器使用不同名称,因此你必须使用重载来定义多个构造器。

调用构造器

构造器是对象方法和类方法的混合体。你可以使用一个对象引用或者一个类引用来调用它。Delphi会传递一个附加的隐含的参数来指示它如何被调用。假如使用一个类引用来调用构造器,Delphi会调用类的NewInstance方法以获得该类的一个新的实例。然后,构造器继续处理并且初始化对象。构造器自动引入一个try-except模块,当构造器中触发异常时,Delphi将自动调用析构器。

使用对象引用来调用构造器时,Delphi不会引入try-except块,也不会调用NewInstance方法。相反,它象调用普通方法一样调用构造器。这个特性允许你调用继承的构造器而无需增加额外的内存开销。

提示
一个常见的错误是尝试使用对象引用来创建一个对象,而不是用一个类引用来创建对象并将它赋值给一个对象引用:

var

  Account: TSavingsAccount;

begin

  Account.Create;                    //错误

  Account := TSavingsAccount.Create; //正确

Delphi的特性之一是你可以控制在何时调用,如何调用,以及是否需要调用一个继承的构造器。这个特性使你可以构建功能强大的类,但在一定程度上也使得错误容易发生。

Delphi总是先构造派生的类,仅当派生类调用了继承的构造器时才去构造基类。在C++中次序相反,从祖先类开始构建,最后才是派生的类。因而,假如有类C继承于B,B继承于A,那么Delphi先是构建C,然后是B最后是A.C++先构建A,然后B,最后C

虚方法和构造器

另一个介于C++和Delphi之间的一个很大的不同是,在C++中,构造器总是根据已经被创建的类的虚方法表来运行。而在Delphi中,虚方法代表了所有派生类的内容,即使基类还没有被创建。因此,当你书写一个可能被构造器调用的虚方法时一定要小心。否则,对象可能还没有完全创建时该方法就被调用了。为了预防这种情况,你应当覆盖AfterConstruction方法,在其中填写需要等到对象被完全创建后才能执行的代码。假如要覆盖AfterConstruction,别忘了调用inherited方法。

一个构造器可以调用另一个构造器。Delphi能够区分该调用是否来自于对象引用,因此调用构造器与调用普通方法相同。调用另一个构造器最常见的理由是把初始化代码放在一个单一的构造器中。例2-7显示了声明和调用构造器的几种不同的方法。

2-7:声明和调用构造器

type

  TCustomer = class ... end;

  TAccount = class

  private

    fBalance: Currency;

    fNumber: Cardinal;

    fCustomer: TCustomer;

  public

    constructor Create(Customer: TCustomer); virtual;

    destructor Destroy; override;

  end;

  TSavingsAccount = class(TAccount)

  private

    fInterestRate: Integer; // Scaled by 1000

  public

    constructor Create(Customer: TCustomer); override; overload;

    constructor Create(Customer: TCustomer; InterestRate: Integer);

        overload;

//注意:TSaveingAccount不需要再定义一个析构器。

//它只是简单的继承了TAccount的构造器

    

  end;

 

var

  AccountNumber: Cardinal = 1;

 

constructor TAccount.Create(Customer: TCustomer);

begin

  inherited Create;             // Call TObject.Create.

  fNumber := AccountNumber;     // Assign a unique account number.

  Inc(AccountNumber);

  fCustomer := Customer;        // Notify customer of new account.

  Customer.AttachAccount(Self);

end;

 

destructor TAccount.Destroy;

begin

  

  //如果在设置fCustomer字段之前构造出错,则该字段为nil

//仅当Customer不为nil才释放account

  if Customer <> nil then

    Customer.ReleaseAccount(Self);

  //调用TObject.Destroy.

  inherited Destroy;

end;

 

const

  DefaultInterestRate = 5000;  // 5%, scaled by 1000

 

constructor TSavingsAccount.Create(Customer: TCustomer);

begin

  //调用同类的另一个构造器

  Create(Customer, DefaultInterestRate);

end;

 

constructor TSavingsAccount(Customer: TCustomer; InterestRate:Integer);

begin

  //调用TAccount.Create

  inherited Create(Customer);

  fInterestRate := InterestRate;

end;

析构器(Destructor)

析构器和构造器一样也隐藏了一个附加的参数。第一次调用时,该附加参数被置为True。这使得Delphi调用FreeInstance来释放对象。如果该析构器调用了继承的析构器,那么Delphi将这个隐含的参数设置为False以防止继承的析构器再次释放同一个对象。

提示:
一个类通常有一个析构器名为DestroyDelphi允许声明多个析构器——但这一特性并未带来什么 方便之处。定义多个析构器通常容易使人感到迷惑并且没有什么实际意义。

Delphi执行析构器中的代码之前,它先调用虚方法BeforeDestruction。你可以覆盖该方法以确保在析构以前有些事务被处理掉。这个特性使你能写出安全的类代码,而不必担心派生类会在何时调用基类的析构器。

提示:
定义一个类时,你可能需要覆盖名为Destroy的析构器方法,但是不要重新定义Free方法。释放一个对象时,你要调用的是Free方法而不是析构器。这一区别非常重要,因为Free首先检查对象引用是否为nil,只有引用非空时才调用Destroy方法。只在某些特定的场合,才需要重新定义Free方法(比如很少用用到的单元VirtIntf中的TInterface类),因为可能调用FreeDestroy更重要。

假如构造器方法和AfterConstruction方法引发了异常, Delphi会自动调用析构器。写一个析构器时,必须意识到正在被撤销的对象有可能没有被完全的创建。Delphi确保所有的字段初始值为空,但假如在构造器中引发了异常,则可能导致某些字段已被初始化而有些未被初始化。如果构造器直接释放对象和指针,那么……其实不必担心这一点,因为Free方法和FreeMem过程都能自动检查指针是否为空。如果构造器调用其他方法,那么也会事先检查指针是否为空。

 

数据库原理选择题总结(第8章)

第八章 1. 属性类型允许是基本数据类型、结构类型和集合类型的模型是()(2001年10月全国卷) A. 平面关系模型 B. 嵌套关系模型 C. 复合对象模型 D. 分布式关系模型 2.“is-par...
  • jonahzheng
  • jonahzheng
  • 2013年09月27日 23:14
  • 983

Delphi 画布对象及其简单应用

 TShape组件的属性,事件和方法 TImage组件的属性,事件和方法 Paintbox组件的属性,事件和方法 使用鼠标事件的绘图第 9章 图像图形应用编程 9.1 图形应用编程本章主...
  • hutao1101175783
  • hutao1101175783
  • 2014年02月09日 19:01
  • 833

delphi 对象与类类型

对象与类类型   从用户角度考虑,用户并不需要了解面向对象编程,就可编写Delphi应用程序。当用户在建立新窗体,添加新构件以及处理事件时,大部分相关代码会由Delphi自动产生。但是,知道语言...
  • hcwp2008
  • hcwp2008
  • 2014年03月23日 14:32
  • 721

ucos iii学习笔记——为什么选择ucos iii

首先我们得先讨论前后台系统和RTOS(Real Time OS)的区别。前后台系统,也即是我们所说的裸机程序,它的结构通常包括一个死循环和若干个中断服务程序,直接上图,我们有一个直观认识: ...
  • lbt111
  • lbt111
  • 2016年09月01日 12:24
  • 634

计算机视觉资源汇总 - Part III

转自:http://www.cvchina.info/codes/ 这个页面力图搜集各种跟CV,AR相关的代码,如无特别声明,均是c/c++代码。还是一贯的标准,不求全面,只求质量。...
  • u012564690
  • u012564690
  • 2014年05月28日 13:58
  • 1092

有关可变形部件模型(Deformable Part Model)的一些说明

关于目标检测中的可变形部件模型(Deformable Part Model)的一些说明
  • masikkk
  • masikkk
  • 2013年12月24日 18:31
  • 14952

目标检测之LatentSVM和可变形部件模型(Deformable Part Model,DPM)

一、综述 DPM和LatentSVM结合用于目标检测由大牛P.Felzenszwalb提出,代表作是以下3篇paper: [1] P. Felzenszwalb, D. McAllester, D...
  • u012564690
  • u012564690
  • 2014年06月03日 21:10
  • 11144

使用Part上传附件

/* * To change this template, choose Tools | Templates * and open the template in the editor. */ ...
  • kcj991932907
  • kcj991932907
  • 2014年11月27日 21:01
  • 630

Linq系列:基础与本质(Part III)

前面我们对LINQ的本质,以及MS针对LINQ对3.5新增的几个类做了分析。作为本系列的第一篇:基础与本质的最后一篇,我想对Lambda表达式讲解一下。本来Lambda表达式也可以放到系列c#3.x学...
  • lishimin1012
  • lishimin1012
  • 2014年07月21日 23:29
  • 368

Delphi中避免使用ClassName判断对象的类型

在公司原有系统的代码中,我看到了很多判别对象的ClassName属性进行分别处理的代码。而且似乎已经是处理类似问题的标准方法。但是其中可能会隐含一些问题。 首先,我们知道多态是面向对象的三大...
  • wozengcong
  • wozengcong
  • 2013年12月19日 01:16
  • 1327
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Delphi对象模型(Part III)
举报原因:
原因补充:

(最多只允许输入30个字)