我有以下两个类:
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; FTest: TList ; public constructor Create(AName: string); destructor Destoroy(); property Name: string read FName; property ItemProps: TList read FProps write FProps; property Test: TList read FTest write FTest; end;
这里是构造函数的代码和TTest类的析构函数:
constructor TTest.Create(AName: string); begin Self.FName := AName; Self.FProps := TList.Create(); Self.FTest := TList .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: TListread FProps; property Test: TList read FTest;
如果任何正文试图写入这些属性,您的代码将导致泄漏.
您是否可以使用TObjectList
而不是TList
将生命周期管理委托给列表类.这往往会导致更简单的代码.
将TList
实例公开为公共属性确实会使您的类暴露于滥用状态.此类的客户端可以调用该类的任何公共方法,并可能以您不期望的方式改变列表,并且不希望满足.您应该寻求更多地封装这些对象.
作为一般规则,您应始终调用类的继承构造函数.在你的情况下,构造函数是TObject
什么都不做的构造函数.但是,最好还是调用它.这样做意味着如果您以后更改继承层次结构,则不会因未调用新父类的构造函数而陷入困境.
constructor TTest.Create(AName: string); begin inherited; FName := AName; FProps := TList.Create(); FTest := TList .Create(); end;
你Self
到处都在使用.这很好,但它根本不是惯用的.我建议你不要这样做,否则你的代码会非常冗长,不易阅读.
您发布的代码中存在大量错误.您的程序中显然有更多未发布的代码.我非常希望代码也包含错误,所以如果在应用上述所有更改后仍然存在泄漏,请不要感到惊讶.