TBaseClass = class
public
destructor Destroy; override;
end;
TFirstClass = class(TBaseClass)
FMyProp: string;
end;
TSecondClass = class(TBaseClass)
FMyFirstClass: TFirstClass;
end;
我需要实现一个能够从中找到所有(对象类型)字段的DESTRUCTOR 相同的基类,并给它一个免费的,以避免所有这些内存泄漏。
为什么呢?因为FMyFirstClass可以创建或不创建,这取决于我的应用程序的流程,我无法保证何时将其创建为Free it,也不想用NIL检查所有析构函数填充代码,因为我有一个很多像这样的领域。
我正在尝试使用新的RTTI来获取基于TBaseClass的所有字段,但我无法获得对象字段的实例,而且我没有想法。
我会走正路吗?你建议做什么?
答案 0 :(得分:8)
调用Free
对nil实例没有影响。它是故意设计的。你的析构函数应该在它逻辑上拥有的任何对象类型的字段上调用Free
,与对象是否被构造无关。
答案 1 :(得分:3)
Delphi对象应始终由另一个对象“拥有”,并且所有者对象负责在调用Destructor时释放拥有的对象。由于这是为每个班级完成的,因此不应该有任何问题。你可以使用RTTI,但它只是时间和资源的一部分,因为编写Destructors非常简单。
示例,基类引入了两个TObject类型字段:
TBaseClass = class
private
public
OwnedObject: TObject;
NotOwnedObject: TObject;
destructor Destroy;override;
end;
destructor TBaseClass.Destroy;override;
begin
OwnedObject.Free; // TBaseClass owns this object, it should be destroyed when
// TBaseClass is destroyed.
// Do NOT call NotOwnedObject.Free: that object is not owned by TBaseClass,
// a different object is responsible for calling Free on it.
// ALWAYS call "inherited" from your destructor, it allows daisy-chaining destructors.
// more on this in the next example.
inherited;
end;
引入新字段的后代类应覆盖析构函数并释放这些对象,然后调用inherited
以使父级有机会释放它引入的对象。例如:
TFirstClass = class(TBaseClass)
public
AnOtherOwnedObject: TObject;
AnOtherNotOwnedObject: TObject;
destructor Destroy;override;
end;
destructor TFirstClass.Destroy;override;
begin
// Free the stuff we owned and *we* introduced:
AnOtherOwnedObject.Free;
// Call the inherited destructor so the base class can free fields they introduced:
inherited;
end;
在你的问题中你说:
我需要实现一个DESTRUCTOR,它能够从同一个基类中找到所有(对象类型)字段并给它一个Free来避免所有这些内存泄漏。
正如您所看到的,这不是这样做的方法:派生类的析构函数调用inherited
,因此基类可以释放它引入的字段。
nil
之前,您无需检查.Free
,因为Free会自行完成。答案 2 :(得分:1)
我认为你没有采取正确的方式。只需释放您声明/创建它们的类中的对象。正如巴里所说,你不需要进行零检查。
答案 3 :(得分:1)
您在其他答案中收到的建议是正确的。确保将每个对象实例化与析构函数中的相应Free
配对。
我想指出为什么你不能使用RTTI来查找所有对象实例并释放它们。某些对象实例可能不属于正在销毁的对象。如果您的字段包含对系统中某个其他实体所拥有的对象的引用,那么您无权将其销毁。 RTTI无法告诉您谁拥有该对象,只有您可以知道。
答案 4 :(得分:-1)
你这样做是错误的。您必须保存引用以了解您必须释放的内容,如果您不知道流程,那么您的设计就是错误。
如果您的需求不同寻常,您可以重构基类来保存所有引用,检查此代码是否可以帮助您:
program BaseFree;
{$APPTYPE CONSOLE}
uses
Classes, SysUtils;
type
TBase = class(TObject)
strict private
class var Instances: TList;
class var Destroying: boolean;
class procedure Clear(Instance: TBase);
private
class procedure Debug;
protected
class constructor Create;
class destructor Destroy;
public
constructor Create;
destructor Destroy; override;
end;
TItem = class(TBase)
end;
{ TBase }
class procedure TBase.Clear(Instance: TBase);
var
I: Integer;
Item: TBase;
begin
for I := 0 to TBase.Instances.Count -1 do
begin
Item:= TBase.Instances[I];
if Item <> Instance then
Item.Destroy;
end;
TBase.Instances.Clear;
end;
class constructor TBase.Create;
begin
TBase.Instances:= TList.Create;
end;
constructor TBase.Create;
begin
if TBase.Destroying then
// instead of raise Exception, you can "wait" and start a new cycle.
raise Exception.Create('Cannot Create new instances while Destrying.');
inherited Create;
TBase.Instances.Add(Self);
end;
class procedure TBase.Debug;
begin
Writeln('TBase.Instances.Count: ', TBase.Instances.Count);
end;
destructor TBase.Destroy;
begin
if not TBase.Destroying then
begin
TBase.Destroying:= True;
try
TBase.Clear(Self);
finally
TBase.Destroying:= False;
end;
end;
inherited;
end;
class destructor TBase.Destroy;
begin
TBase.Clear(nil);
TBase.Instances.Free;
end;
procedure CreateItems(Count: Integer);
var
I: Integer;
Item: TItem;
begin
TBase.Debug;
for I := 0 to Count -1 do
TItem.Create;
TBase.Debug;
Item:= TItem.Create;
TBase.Debug;
Item.Free;
TBase.Debug;
end;
begin
ReportMemoryLeaksOnShutdown:= True;
try
CreateItems(100);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Writeln('Press <ENTER> to finish.');
ReadLn;
end.