我使用Delphi Seattle。
当我尝试释放我创建的对象时,会出现问题。
我在这个网站(以及其他网站)中搜索了已经发布的这个问题的答案,但它们都有点不同。根据这些讨论,我的代码应该可行,但显然有些事情是不对的。
所以,我需要帮助...
执行流程:
a)以fmLoanRequest的形式,我基于Class TStorageLoan(TLoan的一个子类)创建一个对象。构造函数将所有类型的值加载到对象的某些属性中(现在显示在此处)。
b)稍后,我将对象的地址传递给另一个形式(fmLoan)到适当的公共变量。 fmLoan是所有用户与贷款内容交易的形式。请注意,当我们在fmLoan中时,fmLoanRequest保持不变。当fmLoan关闭时,我们将返回fmLoanrequest。
c)显示fmLoan表单(并显示对象中的数据 - 一切运行良好)。
d)关闭fmLoan时,调用一个过程来释放Loan对象 - 如果已分配(参见第二个代码片段的第10行)。这似乎工作正常(没有错误)。
e)执行下面第14行中的代码时出现“无效指针操作”错误:(如果已分配(oLoan),则为oLoan.Free; )。
我添加了这一行,以确保如果fmLoan由于某种原因没有处理它就会释放该对象。我意识到这个对象已经被释放了,但是不应该'if Assgned()'防止不必要的释放对象?
表格fmLoanRequest 的部分代码(我添加了一些行号供参考)
1 // In form fmLoanRequest
2 // Create new Loan Object (from a Loan sub-class as it happens)
3 // Create the object here; Object address will be passed to fmLoan later for handling.
4 oLoan := TStorageLoan.Create(iNewLoanID);
5 ...
6 ...
7 fmLoan.oLoan := oLoan; // pass the address to the other form
8 fmLoan.show;
9 // User would click the 'btnClose' at this point. See event code below.
10 ...
11 ...
12 procedure TfmLoanRequests.btnCloseClick(Sender: TObject);
13 begin
14 if Assigned(oLoan) then oLoan.Free; // <--- ERROR HERE
15 fmLoanRequests.Close;
16 end;
表格fmLoan 的部分代码(我添加了一些行号供参考)
1 //Form fmLoan
2 ...
3 public
4 oLoan : TLoan;
5 ...
6 // In form fmLoan, I call the following upon closing the Form
7 // in the OnClick event of the 'btnClose' button.
8 Procedure TfmLoan.Clear_Loan_Object;
9 begin
10 if Assigned(oLoan) then oLoan.Free; // <-- THIS WORKS FINE
11 end;
我应该尝试不同的方法吗?
我应该删除该行(第14行 - 第一个代码段)并希望获得最佳效果。这完全不是我正确编码的理念!
我是以错误的方式去做的吗?
注意:我显然不使用指针。
任何帮助将不胜感激!
答案 0 :(得分:2)
很明显,您正在释放贷款对象两次,这就是您收到错误的原因。你只需要释放一次。 fmLoanRequests
创建了对象,但您说它可以在fmLoan
关闭之前关闭,因此fmLoan
应取得对象的所有权,并在fmLoan
关闭时释放它。 <{1}}关闭时,请勿释放对象。
另一种方法是定义一个fmLoanRequest
接口,ILoan
和后代实现,然后直接传递TLoan
而不是ILoan
。接口是引用计数的,因此贷款对象将自动释放,并且只有在TLoan
和fmLoanRequests
释放对它的引用后才会释放一次。
答案 1 :(得分:1)
我添加了这一行,以确保
fmLoan
由于某种原因没有处理它时将释放该对象。我意识到这个时候对象已经被释放了,但if Assigned()
不应该阻止不必要的对象释放吗?
这是一个关键的误解。请考虑以下程序:
{$APPTYPE CONSOLE}
var
obj: TObject = nil;
begin
Writeln(Assigned(obj));
obj := TObject.Create;
Writeln(Assigned(obj));
obj.Free;
Writeln(Assigned(obj));
Readln;
end.
这输出以下内容:
FALSE TRUE TRUE
请注意,最后一行输出为TRUE
。换句话说,当您销毁对象时调用其Free
方法时,引用变量未设置为nil
。
您的错误在于您认为Assigned
测试对象是否已被破坏。它没有这样做。它仅测试引用变量是否为nil
。让我们再看一下代码。
obj := TObject.Create;
这里我们创建一个在堆上分配的新对象,调用TObject.Create
。我们还将obj
分配给该对象的地址或引用。执行此行后,obj
是一个包含有效对象地址的引用变量。
obj.Free;
这会破坏obj
引用的对象。运行析构函数,然后销毁内存。执行此行后,该对象已被销毁,但obj
仍然引用已销毁且现在无效的内存。这就是Assigned(obj)
产生真实的原因。
注意:我显然不使用指针。
这是一个有趣的观点。实际上,只要使用引用变量,就会使用指针。尽管该语言隐藏了这一事实,但对象引用变量只不过是指向堆上分配的内存的指针。我们使用术语引用而不是指针,但实际上这些是相同的东西。它们的行为相同,赋值运算符具有相同的语义,您仍然有可能泄漏,双重释放,释放后访问以及指针的所有其他陷阱。因此,尽管您没有明确地使用指针,但仍然需要考虑对象引用变量,就好像它们是指针一样。
我为这个问题写了一个关于这个问题的详细答案。我建议您阅读答案:https://stackoverflow.com/a/8550628/505088。
您将带走的一点是像
这样的代码if Assigned(oLoan) then
oLoan.Free;
毫无意义。 Free
方法还会检查对象引用是否为nil
。这行代码实际上扩展为:
if Assigned(oLoan) then
if Assigned(oLoan) then
oLoan.Destroy;
所以,而不是
if Assigned(oLoan) then
oLoan.Free;
你应该写一下
oLoan.Free;
现在,回到访问冲突。我认为现在显而易见的是,你正试图摧毁一个已经被摧毁的物体。你不能这样做。您需要重新检查您的终身管理。推理像“如果fmLoan
由于某种原因没有处理它”真的不够好。您需要100%确定终身管理。您需要确保您的对象只被销毁一次。无法看到您的所有代码我不想提出具体的建议。
有时一种有用的模式是在销毁对象时将对象引用设置为nil
。如果对象可能在多个地方被销毁,那么可以使用此技术确保您不会尝试将其销毁两次。您甚至可以使用FreeAndNil
辅助函数。但是,值得强调的是,如果您不确定您是否已经销毁了该物体,那么这通常表明设计不佳。如果您发现自己想要添加Free
的电话以“以防万一”,那么您几乎肯定会做出严重错误。