7.5 构造函数
在上面的代码中,为了创建一个类的对象(或为对象分配内存),我调用了 Create 方法。这是一个构造函数,是一种可以应用于类的特殊方法,用于为类的新实例分配内存:
ADay := TDate.Create;
该实例由构造函数返回,可以分配给一个变量,用于存储该对象并在以后使用。创建对象时,其内存将被初始化。新实例的所有数据都被设置为零(或 nil,或空字符串,或给定数据类型的适当 "默认 "值)。
如果希望实例数据一开始的值不是零(尤其是当零值作为默认值意义不大时),则需要编写一个自定义构造函数来实现。新构造函数可以叫 Create,也可以叫其他名字。决定其作用的不是名称,而是constructor
关键字的使用。
注解:换句话说,
Object Pascal
支持命名构造函数,而在许多 OOP 语言中,构造函数必须以类本身命名。有了命名的构造函数,你就可以拥有多个具有相同参数的构造函数(除了重载 Create 符号—重载将在下一节中介绍)。在 OOP 语言中,构造函数还可以是虚构造函数,这是该语言的另一个非常特殊的功能。在下一章介绍虚方法的概念后,我将展示一些关于这个非常好的功能的例子。
为类添加自定义构造函数的主要目的是初始化其数据。如果在创建对象时不对其进行初始化,以后调用方法时可能会出现奇怪的行为,甚至运行时出错。与其等着这些错误出现,不如使用预防性技术从一开始就避免。其中一种技术就是始终使用构造函数来初始化对象的数据。例如,我们必须在创建对象后调用 TDate 类的 SetValue 过程。作为替代方案,我们可以提供一个自定义的构造函数,创建对象并赋予其初始值:
constructor TDate.Create;
begin
FDate := Date; // 今天
end;
constructor TDate.CreateFromValues(M, D, Y: Integer);
begin
FDate := SetValue(M, D, Y);
end;
您可以使用这些构造函数,就像我在Date3示例中所做的那样,在附加到两个独立按钮的代码中:
ADay1 := TDate.Create;
ADay2 := TDate.CreateFromValues(12, 25, 2015);
一般情况下,虽然构造函数可以使用任何名称,但请记住,如果您使用的名称不是 Create,那么基类 TObject 类的 Create 构造函数仍然可用。如果你正在开发和发布供他人使用的代码,调用默认 Create 构造函数的程序员可能会绕过你提供的初始化代码。通过定义一个带有某些参数(或不带参数,如上例)的 Create 构造函数,你就可以用一个新的构造函数替换默认定义,并强制使用它。
与类可以拥有自定义构造函数的方式相同,类也可以拥有自定义析构函数,即使用destructor关键字声明并始终名为 Destroy 的方法。析构方法可以在对象被销毁前执行一些资源清理,但在很多情况下并不需要自定义析构函数。
正如构造函数调用会为对象分配内存一样,析构函数调用也会释放内存。只有在构造函数中或在其生命周期中获取资源(如另一个对象)的对象才真正需要自定义析构函数。
与默认的Create构造函数不同,默认的Destroy析构函数是虚函数的,强烈建议开发人员覆盖这个虚析构函数(虚方法在下一章中介绍)。
这是因为与直接调用析构函数来释放对象不同,调用 TObject 类的特殊 Free 方法是一种良好的 Object Pascal 编程习惯。因此,如果你定义了一个不同名称的析构函数,它就不会被 Free 调用。我们将在第 13 章重点讨论内存管理,届时将再次讨论这个话题。
注解:正如下一章所述,Destroy 是一个虚方法。你可以在继承类中用一个新的定义替换它的基本定义,并用override关键字标记。顺便提一下,静态方法调用虚方法是一种非常常见的编程风格,称为模板模式。在析构函数中,一般只需编写资源清理代码。尽量避免可能引发异常或耗费大量时间的复杂操作,以免给对象清理带来麻烦,而且许多析构函数都是在程序终止时调用的,因此要尽可能保持快速。