递归析构函数
Recursive Destructor
我有以下两个class:
type
TItemProp = class
private
FItemPropName: string;
FItemPropValue: string;
public
constructor Create(AItemPropName, AItemPropValue: string);
class function GetItemProp(AHTMLElement: OleVariant; AProp: string): TItemProp;
property ItemPropName: string read FItemPropName;
property ItemPropValue: string read FItemPropValue;
end;
TTest = class
private
FName: string;
FProps: TList<TItemProp>;
FTest: TList<TTest>;
public
constructor Create(AName: string);
destructor Destoroy();
property Name: string read FName;
property ItemProps: TList<TItemProp> read FProps write FProps;
property Test: TList<TTest> read FTest write FTest;
end;
这里是TTest的构造函数和析构函数的代码class:
constructor TTest.Create(AName: string);
begin
Self.FName := AName;
Self.FProps := TList<TItemProp>.Create();
Self.FTest := TList<TTest>.Create();
end;
destructor TTest.Destoroy();
var
I: Integer;
begin
for I := 0 to Self.FTest.Count - 1 do
begin
Self.FTest[I].Free;
Self.FTest[I] := nil;
end;
Self.FProps.Free;
Self.FTest.TrimExcess;
Self.FTest.Free;
inherited;
end;
问题在于此代码正在泄漏内存。我应该如何重写析构函数以修复内存泄漏?
第一个问题在这里:
destructor Destroy();
您需要覆盖 TObject
中声明的虚拟析构函数。像这样:
destructor Destroy; override;
您的析构函数实现不必要地复杂,并且也无法销毁 FProps
拥有的对象。这个析构函数应该这样写:
destructor TTest.Destroy;
var
I: Integer;
begin
for I := 0 to FTest.Count - 1 do
FTest[I].Free;
FTest.Free;
for I := 0 to FProps.Count - 1 do
FProps[I].Free;
FProps.Free;
inherited;
end;
公开这些列表对象的属性不应该有 setter 方法。因此,它们应该是这样的只读属性:
property ItemProps: TList<TItemProp> read FProps;
property Test: TList<TTest> read FTest;
如果任何人试图写入这些属性,您的代码将导致泄漏。
如果您使用 TObjectList
而不是 TList
,您可以将生命周期管理委派给列表 class。这往往会导致更简单的代码。
将 TList<T>
实例公开为 public 属性确实会使您的 class 受到滥用。此 class 的客户端可以调用 class 的任何 public 方法,并可能以您不期望且不希望迎合的方式改变列表。你应该寻求更多地封装这些对象。
作为一般规则,您应该始终调用 class 的继承构造函数。在您的情况下,构造函数是 TObject
什么都不做的构造函数。但是,无论如何调用它是一个好习惯。这样做意味着如果您以后更改继承层次结构,您将不会因为不调用新父级的构造函数而被抓到 class.
constructor TTest.Create(AName: string);
begin
inherited;
FName := AName;
FProps := TList<TItemProp>.Create();
FTest := TList<TTest>.Create();
end;
您到处都在使用 Self
。这很好,但它一点也不惯用。我建议你不要养成这个习惯,否则你的代码会很冗长,不那么容易阅读。
您发布的代码中存在大量错误。您的程序中显然有更多您尚未发布的代码。我非常希望该代码也包含错误,因此即使在应用上述所有更改后仍然存在泄漏也不要感到惊讶。
我有以下两个class:
type
TItemProp = class
private
FItemPropName: string;
FItemPropValue: string;
public
constructor Create(AItemPropName, AItemPropValue: string);
class function GetItemProp(AHTMLElement: OleVariant; AProp: string): TItemProp;
property ItemPropName: string read FItemPropName;
property ItemPropValue: string read FItemPropValue;
end;
TTest = class
private
FName: string;
FProps: TList<TItemProp>;
FTest: TList<TTest>;
public
constructor Create(AName: string);
destructor Destoroy();
property Name: string read FName;
property ItemProps: TList<TItemProp> read FProps write FProps;
property Test: TList<TTest> read FTest write FTest;
end;
这里是TTest的构造函数和析构函数的代码class:
constructor TTest.Create(AName: string);
begin
Self.FName := AName;
Self.FProps := TList<TItemProp>.Create();
Self.FTest := TList<TTest>.Create();
end;
destructor TTest.Destoroy();
var
I: Integer;
begin
for I := 0 to Self.FTest.Count - 1 do
begin
Self.FTest[I].Free;
Self.FTest[I] := nil;
end;
Self.FProps.Free;
Self.FTest.TrimExcess;
Self.FTest.Free;
inherited;
end;
问题在于此代码正在泄漏内存。我应该如何重写析构函数以修复内存泄漏?
第一个问题在这里:
destructor Destroy();
您需要覆盖 TObject
中声明的虚拟析构函数。像这样:
destructor Destroy; override;
您的析构函数实现不必要地复杂,并且也无法销毁 FProps
拥有的对象。这个析构函数应该这样写:
destructor TTest.Destroy;
var
I: Integer;
begin
for I := 0 to FTest.Count - 1 do
FTest[I].Free;
FTest.Free;
for I := 0 to FProps.Count - 1 do
FProps[I].Free;
FProps.Free;
inherited;
end;
公开这些列表对象的属性不应该有 setter 方法。因此,它们应该是这样的只读属性:
property ItemProps: TList<TItemProp> read FProps;
property Test: TList<TTest> read FTest;
如果任何人试图写入这些属性,您的代码将导致泄漏。
如果您使用 TObjectList
而不是 TList
,您可以将生命周期管理委派给列表 class。这往往会导致更简单的代码。
将 TList<T>
实例公开为 public 属性确实会使您的 class 受到滥用。此 class 的客户端可以调用 class 的任何 public 方法,并可能以您不期望且不希望迎合的方式改变列表。你应该寻求更多地封装这些对象。
作为一般规则,您应该始终调用 class 的继承构造函数。在您的情况下,构造函数是 TObject
什么都不做的构造函数。但是,无论如何调用它是一个好习惯。这样做意味着如果您以后更改继承层次结构,您将不会因为不调用新父级的构造函数而被抓到 class.
constructor TTest.Create(AName: string);
begin
inherited;
FName := AName;
FProps := TList<TItemProp>.Create();
FTest := TList<TTest>.Create();
end;
您到处都在使用 Self
。这很好,但它一点也不惯用。我建议你不要养成这个习惯,否则你的代码会很冗长,不那么容易阅读。
您发布的代码中存在大量错误。您的程序中显然有更多您尚未发布的代码。我非常希望该代码也包含错误,因此即使在应用上述所有更改后仍然存在泄漏也不要感到惊讶。