先谢谢blazingfire在刚才的贴子中,突然想到一个关于FreeAndNil的问题
众所周知:FreeAndNil:=(Object.Free)+(Object:=Nil)
让我们先看一下FreeAndNil和Free的实现
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;
显然是先(Object:=Nil)而后(Object.Free)
这带来一个问题:注意Free过程,是先判断对象引用是否为空,而后Destroy的,
结论是经过FreeAndNil的,if Self<>nil then 为永假式!从来就不执行Destroy~最终结论:
在释放对象时仅将对象引用置为空就OK了?那Delphi如何在堆链表中将该块地址置为空闲状态,以便以后使用!
众所周知:FreeAndNil:=(Object.Free)+(Object:=Nil)
让我们先看一下FreeAndNil和Free的实现
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
procedure TObject.Free;
begin
if Self <> nil then
Destroy;
end;
显然是先(Object:=Nil)而后(Object.Free)
这带来一个问题:注意Free过程,是先判断对象引用是否为空,而后Destroy的,
结论是经过FreeAndNil的,if Self<>nil then 为永假式!从来就不执行Destroy~最终结论:
在释放对象时仅将对象引用置为空就OK了?那Delphi如何在堆链表中将该块地址置为空闲状态,以便以后使用!
解决方案 »
- stringgrid 分页显示
- delphi向sap传入数据出现乱码
- SQL2008中,数据类型为:image 在DELPHI中如果转换为字符再保存到文本文件中?
- 请教高手,delphi中float的有效位多少,怎么改成15位的有效位?
- 怎么样在Dbgrid中实现按住shift键把鼠标点选两个位置之间的记录全部选中,像Excel那样
- 请问Toolbutton的动态生成问题???
- 按装发布问题。
- 如何把Access数据库集成一个exe文件?
- 请高手将这段cpp代码转换成delphi的代码(很短的一段代码)
- 高分求一弱智问题,如何求1,2,3,4,...n的最小公倍数
- VB版块没人理,只有回老家了,这个VBA错在那里?
- dbgrideh怎样用复选框实现批量删除?
但free的是Temp,判断的也是Temp是否为nil.....
procedure TForm1.Button2Click(Sender: TObject);
var
p1, p2: Pchar;
begin
p1 := AllocMem(100);
p2 := p1;
p1 := nil;
FreeMem(p2);
end;
这个和FreeAndNil是相同的道理。
目前我的核心问题
1.
FreeAndNil这么写不行?procedure FreeAndNil(var Obj);
begin
TObject(Obj).Free;
Pointer(Obj) := nil;
end;
为什么要用一个Temp2.
Obj.Free会不会不执行Obj本类的析构函数,而直接直接根类的Free
------
这个问题以前记得有人问过,以前都没搞懂~
TObject(Obj).Free;
Pointer(Obj) := nil;
这样写,如果在free时出异常,:= nil就不会被执行了所以首先确保Pointer(Obj):=nil; 再去free2.
析构函数是Destroy,不是free.只是free里面调用了Destroy. 除非你给你的类定义自己的free,否则都是执行Tojbect的free,而Tobject的free调用Tobject的Destroy,这就是为什么delphi里面把Tobject的Destroy设计为虚函数."Obj.Free会不会不执行Obj本类的析构函数,而直接直接根类的Free"
综合上面说的,的确会有这种情况,比如
-你的类没有定义free.当你执行Tyourclass.free时,实际调用的是Tobject.free. 这时候,如果你的析构函数Destroy没有用override重写基类的Destroy的话,那这个时候调用的就是Tobject.Destroy;如果你的Destroy没有用override,那你必须自己再写个free方法来调用这个析构函数.
===============
这个问题是一个关于virtual/dynamic method的执行问题(constructor里面执行的时候例外)
可以这样:
Try
TObject(Obj).Free
Finally
Pointer(Obj):=Nil;
end;"所以首先确保Pointer(Obj):=nil; 再去free "
如果先赋值为Nil,而后Free发生异常怎么办?谁去负责把该块内存的使用标识置为可用?第二个还有个小问题:
假设有一类MyClass,继承自TObject,且其内有Destroy,如果用MyClass.Free,则直接将其变为TObject类型后,Free,谁负责MyClass中的资源回收?
freeandnil遇到异常也是要抛出来的,你try了异常就被截了,难道在finally里再throw出来? 那还不如用个temp缓存下,先nil再free"如果先赋值为Nil,而后Free发生异常怎么办?谁去负责把该块内存的使用标识置为可用?"
temp的生存期只到free函数结束.结束时候temp本身这个引用(可以理解为指针)就被回收了. 而free发生异常只可能是没有分配内存而要强制回收(没有create出实例),所以"谁去负责把该块内存的使用标识置为可用"这个没必要担心吧
MyClass.Free调用的就是Tobject.free,Tobject.free再调用Tobject.Destroy
所以,如果MyClass需要在析构时做自定义的处理,必须override一个Destroy.这样Tobject的Destroy实际就是调用了MyClass的Destroy. 这就是为什么delphi要设计Tobject的Destroy是个纯虚函数.举个例子:
type
Taaa = class(Tobject)
destructor Destroy;override;
end;destructor Taaa.Destroy;
begin
showmessage('aaa');
end;你执行Taaa.free会执行showmessage('aaa');
但如果不用override:
Taaa = class(Tobject)
destructor Destroy;
end;Taaa.free就不会调用showmessage('aaa');
procedure FreeAndNil(var Obj)函数来说,其参数类型是引用类型(也是通过传址的方式来实现的),而这个类型本身又是一个
对象引用。Temp := TObject(Obj)只是提取Obj所指向的对象的引用到temp;Pointer(Obj) := nil就是把传进来的形参本身置空,而
这个过程就会导致Obj对应的实参也会变为空(因为参数类型是引用类型嘛)!如:
var
obj: TObject;
begin
obj := TObject.Create;
FreeAndNil(obj);//==>obj:=nil;
if obj <> nil then
ShowMessage('Obj不为空!');
end;
再看Temp.Free因为temp引用已经得到了参数所代表对象的引用,它再Free就可以调用到对象的析构函数了,从而对于达到释放内存
的目的。
至于你说的“结论是经过FreeAndNil的,if Self <>nil then 为永假式!从来就不执行Destroy~”,显然是不对的!这个是类和对象
的区别和联系,这样讲吧,对于同一个类我们可以创建多个此类的对象。而我们再进行面向对象的编程中,实际上是针对类来编程,而
不是对象,这点我想你不会不同意吧?!当你在写一个类TA的成员方法,而这个方法又来访问这个类的成员变量,如下示:
type
TA = class
private
Val: Integer;
public
function GetVal: integer;
constructor Create(initVal: Integer);
end;implementation{ TA }constructor TA.Create(initVal: Integer);
begin
Val := initVal;
end;function TA.GetVal: integer;
begin
Result := Val;
end;
如果我们创建两个TA对象如下:
a1 := TA.Create(1); a2 := TA.Create(3);那么你再分别调用a1,a2的getVal,那到底是多少呢?是1还是3
我想你在设计这个类的时候时根本就不知道Val成员是多少,或者换一个角度来讲,你根本不关心Val是多少
因为你相信Val会是正确(也就是GetVal能返回正确的值),更深一个层次来讲:你知道当TA的一个对象的值产生了以后,这个Val成员应该有一个
自己的值,就算自己修改了Val,也不会影响其它的TA对象的Val。为了作了这一点,Delphi就必须保证在设计类时操作一个类的成员的时候必须
是绑定到一个具体的对象上的! 这一点就是Self这个关键字的功能。可能说到这里,问题差不多就解决!
当我们调用a1.getValue,其实是在调用function TA.GetVal: integer时实际上是返回了a1.Val成员,它等价与Self.Val,而调用
a2.getValue实际上返回了a2.Val成员,它也等价与Self.Val。细心的你可能已经看出来了其实在a1.GetValue时,Self表示的是a1;
而a2.getValue时,Self表示的是a2。也就是在执行类的成员方法时Self表示是 当前调用这个成员方法的对象的本身!!也许你可能说我是在猜
想,那请看下面这个实例:
type
TA = class
private
Val: Integer;
public
function GetVal: integer;
function GetSelfAddr: Integer;
constructor Create(initVal: Integer);
end;{ TA }constructor TA.Create(initVal: Integer);
begin
Val := initVal;
end;function TA.GetVal: integer;
begin
Result := Val;
end;function TA.GetSelfAddr: Integer;
begin
Result := Integer(Self);
end;procedure TForm1.Button1Click(Sender: TObject);
var
a1, a2: Ta;
begin
a1 := TA.Create(1);
a2 := TA.Create(3); if Integer(a1) = a1.GetSelfAddr then
ShowMessage('Now Self=a1')
else
ShowMessage('Now Self<>a1'); if Integer(a2) = a2.GetSelfAddr then
ShowMessage('Now Self=a2')
else
ShowMessage('Now Self<>a2'); a1.Free;
a2.Free;
end;
简单的实验一下,你会发现当a1:Ta再调用Ta.GetSelfAddr方法时,self==a1;当a2:Ta再调用Ta.GetSelfAddr方法时,self==a2;
也就是Self是 动态关联到每个调用类的成员方法 具体对象的!
所以“结论是经过FreeAndNil的,if Self <>nil then 为永假式!从来就不执行Destroy~ ”当然就不对了,因为用不同的对象在调用
Free时,其实Self是不一样的!
我要说的就是如果你的MyClass以一种特殊的方式去析构的话(覆盖了Destroy方法),即使你只有MyClass父类的引用,那么就是父类的Destroy释放方法,
也会最终执行到你覆盖以后的MyClass的Destroy方法