7.5.1 使用构造函数和析构函数管理本地类数据
即使我将在本书后面介绍更复杂的情况,这里我也想向你展示一个使用构造函数和析构函数进行资源保护的简单案例。 这是使用析构函数最常见的情况。
假设您有一个具有以下结构的类(也是Date3示例的一部分):
type
TPerson = class
private
FName: string;
FBirthdate: TDate;
public
constructor Create(const Name: string);
destructor Destroy; override;
// 一些实际方法
function Info: string;
end;
该类引用了另一个名为 FBirthdate 的内部对象。创建 TPerson 类的实例时,也应创建该内部(或子)对象;销毁实例时,也应释放该内部(或子)对象。
下面是构造函数、重载析构函数和内部方法的代码编写方法,这些方法总是想当然地认为内部对象是存在的:
constructor TPerson.Create(const Name: string);
begin
FName := Name;
FBirthdate := TDate.Create;
end;
destructor TPerson.Destroy;
begin
FBirthdate.Free;
inherited;
end;
function TPerson.Info: string;
begin
Result := FName + ': ' + FBirthdate.GetText;
end;
注解:要理解用于定义析构函数的override关键字和定义中的 inherited 关键字,需要等到下一章。现在只需说明前者用于表示类有一个新的定义来替换基类的析构函数,而后者用于调用基类的析构函数。还要注意的是,在方法声明中使用了 override,但在方法实现代码中没有使用。
现在,您可以像下面的情况一样使用外部类的对象,而内部对象将在 TPerson 对象创建时正确创建,并在 TPerson 销毁时及时销毁:
var
Person: TPerson;
begin
Person := TPerson.Create('John');
// 使用类及其内部对象
Show(Person.Info);
Person.Free;
end;
同样,您可以在Dates3示例中找到的这部分代码。
7.5.2 重载方法和构造函数
Object Pascal
支持重载函数和方法:只要参数不同,就可以有多个同名的方法。我们已经了解了重载在全局函数和过程中的作用,同样的规则也适用于方法。通过检查参数,编译器可以确定要调用的方法版本。
同样,有两个基本的重载规则:
- 每个方法的版本后面必须跟有overload关键字。
- 差异必须是形参的数量或类型,或两者都有。返回类型不能用来区分两个方法。
如果可以将重载应用于类的所有方法,那么这个特性对于构造函数就尤为重要,因为我们可以有多个构造函数并将它们全部称为Create
,这使它们易于记忆。
历史:从历史上看,C++专门增加了重载功能,以允许使用多个构造函数,因为构造函数必须具有相同的名称(类的名称)。在Object Pascal中,这个功能可能被认为是不必要的,仅仅是因为多个构造函数可以有不同的具体名称,但重载还是被添加到了语言中,因为它在许多其他场景中也很有用。
作为重载的一个示例,我向TDate类添加了两个不同版本的SetValue方法:
type
TDate = class
public
procedure SetValue(Month, Day, Year: Integer); overload;
procedure SetValue(NewDate: TDateTime); overload;
end;
procedure TDate.SetValue(Month, Day, Year: Integer);
begin
FDate := EncodeDate(Year, Month, Day);
end;
procedure TDate.SetValue(NewDate: TDateTime);
begin
FDate := NewDate;
end;
在这一简单步骤之后,我为该类添加了两个单独的Create构造函数,一个没有参数,它隐藏了默认构造函数,另一个带有初始化值。没有参数的构造函数使用今天的日期作为默认值:
type
TDate = class
public
constructor Create; overload;
constructor Create(Month, Day, Year: Integer); overload;
end;
constructor TDate.Create(Month, Day, Year: Integer);
begin
FDate := EncodeDate(Year, Month, Day);
end;
constructor TDate.Create;
begin
FDate := Date; // 今天
end;
有了这两个构造函数,就可以以两种不同的方式定义新的TDate对象:
var
Day1, Day2: TDate;
begin
Day1 := TDate.Create(2020, 12, 25);
Day2 := TDate.Create; // 今天
这段代码是Dates4示例的一部分。