内存泄漏与访问冲突,class 设计问题
memory leak vs. access violation, class design issue
我在下面的代码中为用户 class 展示了一个简短的设计模式。
type
MytestClass = class
alist: TStringlist;
public
constructor Create;
destructor destroy; override;
end;
{ MytestClass }
type
TForm1 = class(TForm)
btn_version01: TBitBtn;
btnversion02: TBitBtn;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure btn_version01Click(Sender: TObject);
procedure btnversion02Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
btestClass : MytestClass;
aComplexClassDesign : TComplexClassDesign;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor MytestClass.Create;
begin
alist := TStringlist.Create;
end;
destructor MytestClass.destroy;
begin
alist.free;
inherited;
end;
procedure TForm1.btnversion02Click(Sender: TObject);
var atestClass : MytestClass;
begin
///
atestClass :=MytestClass.Create;
atestClass.Free;
atestClass := nil;
end;
procedure TForm1.btn_version01Click(Sender: TObject);
var atestClass : MytestClass;
begin
///
atestClass :=MytestClass.Create;
atestClass.Free;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
btestClass.free;
aComplexClassDesign.Free;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
btestClass :=MytestClass.Create;
aComplexClassDesign :=TComplexClassDesign.Create;
end;
end.
希望这是一个没有内存泄漏和访问冲突的完美设计。我在实际应用中使用的所有 classes 都是按照这种模式设计的。
在上面的代码中,FastMM4 在我的 TComplexClassDesign 上没有显示任何问题。在实际应用程序中,FASTMM4 报告了我的 TComplexClassDesign 的内存泄漏,甚至我在窗体的关闭事件中调用了自由函数。如果我单步执行代码以确保执行此函数。
知道如何调试此内存泄漏报告,是否有任何选项可以查看尚未发布的 TComplexClassDesign 实例?我收到此奇怪的内存泄漏报告的任何其他原因?
奖金问题:
DUnit 总是像这样制作拆解代码
atestClass :=MytestClass.Create;
atestClass.Free;
atestClass := Nil
真的需要最后一行代码吗?
奖励答案:
即使您调用了 free,atestClass 仍将指向一个内存地址。如果您在调用 free 后尝试访问内存,如果其他东西正在使用该内存位置,您可能会访问错误的数据,或者如果内存不再可访问,则会发生访问冲突。
如果 free 是函数的最后一行然后它 exits/returns 那么它可能不需要...
将 OnCreate
与 OnClose
匹配是错误的。这些事件不是一对。 OnCreate
事件在构建期间被调用。销毁的匹配事件是OnDestroy
。您在 OnCreate
中创建的任何内容都应在 OnDestroy
中销毁。
就我个人而言,我认为 OnCreate
和 OnDestroy
没有什么价值。我总是会选择覆盖虚拟构造函数和析构函数。
procedure TForm1.FormCreate(Sender: TObject);
begin
btestClass :=MytestClass.Create;
aComplexClassDesign :=TComplexClassDesign.Create;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
btestClass.free;
aComplexClassDesign.Free;
end;
作为一般规则,以相反的创建顺序销毁对象。这在这里无关紧要,但如果对象具有依赖性,那么顺序有时很重要。
procedure TForm1.btnversion02Click(Sender: TObject);
var atestClass : MytestClass;
begin
atestClass :=MytestClass.Create;
atestClass.Free;
atestClass := nil;
end;
将 nil
分配给 atestClass
有什么意义吗?绝对不。此变量在赋值后立即离开作用域,因此任何其他代码都无法观察到结果赋值。我希望编译器对此发出警告。当您进行赋值但从不使用分配的值时,它会发出警告。我希望你已经启用了警告。
最后,为什么你的真实代码会出现内存泄漏?不可能从这里说出来。 FastMM 的完整调试版本提供带有信息堆栈跟踪的泄漏报告。这些跟踪将包括导致分配泄漏对象的调用堆栈。
我在下面的代码中为用户 class 展示了一个简短的设计模式。
type
MytestClass = class
alist: TStringlist;
public
constructor Create;
destructor destroy; override;
end;
{ MytestClass }
type
TForm1 = class(TForm)
btn_version01: TBitBtn;
btnversion02: TBitBtn;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure btn_version01Click(Sender: TObject);
procedure btnversion02Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
btestClass : MytestClass;
aComplexClassDesign : TComplexClassDesign;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor MytestClass.Create;
begin
alist := TStringlist.Create;
end;
destructor MytestClass.destroy;
begin
alist.free;
inherited;
end;
procedure TForm1.btnversion02Click(Sender: TObject);
var atestClass : MytestClass;
begin
///
atestClass :=MytestClass.Create;
atestClass.Free;
atestClass := nil;
end;
procedure TForm1.btn_version01Click(Sender: TObject);
var atestClass : MytestClass;
begin
///
atestClass :=MytestClass.Create;
atestClass.Free;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
btestClass.free;
aComplexClassDesign.Free;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
btestClass :=MytestClass.Create;
aComplexClassDesign :=TComplexClassDesign.Create;
end;
end.
希望这是一个没有内存泄漏和访问冲突的完美设计。我在实际应用中使用的所有 classes 都是按照这种模式设计的。
在上面的代码中,FastMM4 在我的 TComplexClassDesign 上没有显示任何问题。在实际应用程序中,FASTMM4 报告了我的 TComplexClassDesign 的内存泄漏,甚至我在窗体的关闭事件中调用了自由函数。如果我单步执行代码以确保执行此函数。 知道如何调试此内存泄漏报告,是否有任何选项可以查看尚未发布的 TComplexClassDesign 实例?我收到此奇怪的内存泄漏报告的任何其他原因?
奖金问题:
DUnit 总是像这样制作拆解代码
atestClass :=MytestClass.Create;
atestClass.Free;
atestClass := Nil
真的需要最后一行代码吗?
奖励答案:
即使您调用了 free,atestClass 仍将指向一个内存地址。如果您在调用 free 后尝试访问内存,如果其他东西正在使用该内存位置,您可能会访问错误的数据,或者如果内存不再可访问,则会发生访问冲突。
如果 free 是函数的最后一行然后它 exits/returns 那么它可能不需要...
将 OnCreate
与 OnClose
匹配是错误的。这些事件不是一对。 OnCreate
事件在构建期间被调用。销毁的匹配事件是OnDestroy
。您在 OnCreate
中创建的任何内容都应在 OnDestroy
中销毁。
就我个人而言,我认为 OnCreate
和 OnDestroy
没有什么价值。我总是会选择覆盖虚拟构造函数和析构函数。
procedure TForm1.FormCreate(Sender: TObject);
begin
btestClass :=MytestClass.Create;
aComplexClassDesign :=TComplexClassDesign.Create;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
btestClass.free;
aComplexClassDesign.Free;
end;
作为一般规则,以相反的创建顺序销毁对象。这在这里无关紧要,但如果对象具有依赖性,那么顺序有时很重要。
procedure TForm1.btnversion02Click(Sender: TObject);
var atestClass : MytestClass;
begin
atestClass :=MytestClass.Create;
atestClass.Free;
atestClass := nil;
end;
将 nil
分配给 atestClass
有什么意义吗?绝对不。此变量在赋值后立即离开作用域,因此任何其他代码都无法观察到结果赋值。我希望编译器对此发出警告。当您进行赋值但从不使用分配的值时,它会发出警告。我希望你已经启用了警告。
最后,为什么你的真实代码会出现内存泄漏?不可能从这里说出来。 FastMM 的完整调试版本提供带有信息堆栈跟踪的泄漏报告。这些跟踪将包括导致分配泄漏对象的调用堆栈。