Object Pascal Free方法的奇怪现象,涉及到面向对象理论,请高手指点。(200分)

  • 主题发起人 ZXW49362727
  • 开始时间
Z

ZXW49362727

Unregistered / Unconfirmed
GUEST, unregistred user!
请看以下代码:
var
Strings: TStringList;
<1>: Strings:= nil;
Strings.Free

//为何以上代码运行不会出错?如果Free方法确实调用了,那么它保存在内存哪里呢?
<2>: Strings:= TStringsList.Create;
Strings.Free;
Strings.Free;
//出错。如果上面那个问题解决的话,这个问题也解决了。
Strings:= TStringsList.Create;
Strings.Free;
Strings:= nil;
Strings.Free;
Free的VCL源码如下:
if Self<>nil then
Destory;
确实,如果String=nil 时,不会调用Destory所以不出错。
但问题是Free不是一个静态方法,在没在调用Create的时候,Free方法的代码保存在哪呢?
请高手指教。
 
这个可以 nil.free;
if assign(Strings) then Strings.free
 
回gxcooo:这样做是可以不出错。但我问得不是这个。
 
是很奇怪的现象。在java中也有类似的情况,比如toString()函数就是这样,
即使这个对象指向的是null,仍然可以调用不是静态方法的toString()。
我想可能是这样,在定义了一个对象指针之后,会有一些初始化的过程,这个时候可以调用
一些静态的方法,比如class function ClassName: ShortString,而这个ClassName的获得
方法是这样的:
asm
{ -> EAX VMT }
{ EDX Pointer to result string }
PUSH ESI
PUSH EDI
MOV EDI,EDX
MOV ESI,[EAX].vmtClassName //请注意这里
XOR ECX,ECX
MOV CL,[ESI]
INC ECX
REP MOVSB
POP EDI
POP ESI
上面有行汇编代码: MOV ESI,[EAX].vmtClassName
大家注意vmtCalssName,这个东西在对象变量没有实例的时候是有地址的,因为vmt(虚方法表)
是每个类所拥有的,不是具体的对象所拥有的,我觉得这个东西有些类似类静态变量,
虽然delphi没有这个东西。这样我们再看看Free方法:
procedure TObject.Free;
asm
TEST EAX,EAX
JE @@exit
MOV ECX,[EAX]
MOV DL,1
CALL dword ptr [ECX].vmtDestroy //这里,又是vmt...
@@exit:
end;
大家看到了一个类似的东西,就是[ECX].vmtDestroy,所以事实上不必担心这里没有东西可以调用。
但是,问题是Free的地址在哪里呢?我也不清楚,后来在帮助文档中看见了这样一段话:
Free automatically calls the destructor if the object reference is not nil.
...[red]Unlike Destroy, Free is successful even if the object is nil, so if
the object was never initialized, Free won't result in an error.[/red]

不像析构函数Destroy, Free即使在对象是nil的时候也一样可以成功调用,所以当对象没有
初始化的时候,Free方法也不会出错。[:D]靠!
 
>>但问题是Free不是一个静态方法,在没在调用Create的时候,Free方法的代码保存在哪呢?
有点不明白这句话是什么意思? :)
 
procedure TObject.Free;
asm
TEST EAX,EAX
JE @@exit
MOV ECX,[EAX]
MOV DL,1
CALL dword ptr [ECX].vmtDestroy //这里,又是vmt...
@@exit:
End;
不是已经很明白了吗,
Test EAx,EAx
Je @@exit //如果EAx=0退出,也就是说即使对象为空也没有关系,而且这段程序总是存在的

 
to ZXW49362727:
你只要使用vcl,建有对象,就会有TObject存在,TObject.free的代码就存在。
 
1.因为Strings:= nil;了所以 Strings.Free
不会错
请看 free的代码: asm test eax ,eax
je @@exit
就是判断对象为nil就退出

2,Strings.Free;并没有将strings置为nil只是释放了它占的空间,所以再调就会出错
如需再释放时同时置为nil请用freeandnil();

 
有点意思,这个问题平常还真没仔细想过,呵呵。
楼上的,声明一个对象并不等于建立了对象。
 
我认为这个识编译器专门设置的特殊方法,和其他的方法处理起来不一样。楼上有几位对于
对象方法代码和对象方法存在的地址还没有搞清楚,教父说得对,对象声明了,并不表示对象
就建立了,而按照面向对象原则,一个对象没有建立之前,除了静态方法和变量,对象的方法
是没有分配地址的,也就是说,不能用。可见Free是很特殊的。我在帮助文档中也就看见了一
句话,就是那句:Unlike Destroy, Free is successful even if the object is nil, so if
the object was never initialized, Free won't result in an error.
(翻译)不像析构函数Destroy, Free即使在对象是nil的时候也一样可以成功调用,所以当对象没有
初始化的时候,Free方法不会出错.
至于怎样实现这种特殊效果,我不清楚,那位高人指点一下?
 
to sadj:Free is successful even if the object is nil,我想这只是因为它在调用
destroy之前判断了对象是否为nil,是的话就退出,这应该不算是什么特殊技术,而且
我觉得Delphi不应该对这一个函数进行特殊处理。
 
如果跟踪的话,Free 是可以跟踪进去的,也就是说一个对象即使没有创建,他都有Free
的代码,但是如果用Destroy的话,就根本找不到程序入口点(这一点可以理解)。
 
好象是很怪
 
可以理解。
这也正是类方法和对象方法的不同之处。
 
一个对象的实例被创建只是拥有属于他的一段数据空间
静态方法不属于他
当调用一个静态方法时,编译器产生该方法的一个唯一的名字
当调用Strings.Free时编译器就产生相应的代码,假设就叫TObject.Free
其实就是这样
procedure TObject.Free(Self: TObject);
begin
...
end;
Self为nil
当然可以
就时一个简单的过程而已
可以试一下
procedure TA.A;
begin
ShowMessage('gyb');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
a: TA;
begin
a := nil;
a.A;
end;
而不是说Free有什么特殊的地方
 
〉〉但问题是Free不是一个静态方法,
free是静态方法,在声明的时候就知道它的位置了。
 
实例(数据)归实例,函数归函数
在c++语言
class a
{int b

void f(int i)
{b=i;}
};
如果用c来写,相当于
struct a
{int b
}

void a_f(a* s,int i)
{
s->b=i;
}
数据归数据,函数归函数
其实,c++也是这样实现的,a.f(1)它是用 @a@f$i(a,1)来实现的(@a@f$i只是个函数名字,实际c++把它变得比这个更加古怪一点,包含了参数信息),
而我们用c++只是在写程序的时候方便一点罢了。

这样实例可能不存在,但是函数是一段固定的代码肯定存在,
实例作为参数传递到这个函数,如果函数并没有引用实例内的数据,那么你即使使用
非法的对象指针也不会出错
 
顶部