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

  • 主题发起人 ZXW49362727
  • 开始时间
1:Free肯定不是静态方法,这点可以通过VCL源码知道。
你说的静态方法是指的c++里的静态方法吧?
(因为在delphi就没有c++里的静态方法,所以这个是我的意思表达有问题)。

2;我认为最有可能的Delphi对Free方法特殊处理过。
在声明一个对象时,就已经给他指定了一个内存位置;而就在这个内存位置free已经是已知的了,
而Destroy 却是要到运行时才知道,这也就是我为什么说free是一个静态方法的原因。(这里的静态
不是c++里静态的意思)。
 
唉,送佛送到西天,帮人帮到底。savenight你理解的不对。
所谓静态方法就是类静态方法,它不需要创建一个类的实例就可以调用,在编译的时候就
确定了内存地址,在c++中使用static 标志,在Delphi中是在类的函数或者过程前面加class
来表示。
我跟踪了一个对象引用的使用,发现在声明之后(不创建),对象就有了方法的地址,无论是free还是
destroy还是其他的什么方法,甚至是abstract的方法,内存地址都有了,这里是很奇怪。所以你的第二
个解释也不正确。至于为什么free可以在nil的时候执行,而destroy不行呢?那就是virtual起的作用了。
 
sadj:谢谢。 你说的我知道,我不至于那么弱。:)
procedure TObject.Free;
begin
if Self <> nil then
Destroy
//这里的方法是virtual的,而free不用变。所以我说free是static的(英文书上有这种说法)
//当然,我现在知道你们说的static的意思。我想这些基本概念不用再讨论了。
end;

第二个问题我记得在哪个英文杂志上看过,我从中午找到现在找了3个小时也没找到,
只记得原文中提到“在声明一个对象时,就已经给他指定了一个内存位置,。。。。”后面的就想不起来了,
不过md6里有段话
“The reason is that Free is a known method at a given memory location, whereas
the virtual function Destroy is determined at run time by looking at the type of the object, a
very dangerous operation if the object doesn’t exist any more.”
所以我才综合了一下,写出上面的话。

至于Pipi.讲的这些我也是上个星期才知道的,那时有个人在本论坛问下面的代码为什么能执行?
var
obj:tmyobject;
begin
obj.create;
....
对于这个问题我想了一夜才想出来(自己认为),就是Pipi.现在所说的。

〉〉但是说错了还要自以为是就很难进步了。
有Pipi.和王寒松在,我还敢自以为是? 我只是觉得你要把原因讲明白,不要用训斥人的态度。



 
终于找到了!

The implementation of the [red]static[/red] TObject.Free is in assembler, but in
Pascal it would look like this:
procedure TObject.Free;
begin
if Assigned(Self) then
Destroy
end

This is a small, static method that does a brief safety check, ensuring it is not called though a nil reference.
Assuming the reference looks fine, it will call the virtual (polymorphic) Destroy method,
ensuring the appropriate destruction occurs for whatever object is being accessed.
 
不过,“在声明一个对象时,就已经给他指定了一个内存位置,。。。。”这段英文还没找到,正在找。。。
 
赫赫,说的好呀,不错!!
 
讨论问题而已嘛,何必动肝火呢,呵呵

to sadj:书上说如果声明方法时没有加方法指示字,该方法就是静态的,这一点跟C++不
一样。还有你说的声明一个对象后,它的virtual或是abstract方法的入口地址都已经确定
了,这显然是不对的。
你试试下面代码:
TTest = class
public
procedure b;virtual;
end;
procedure TTest.b;
begin
ShowMessage('b');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
bb : TTest;
begin
bb.b
//典型的非法访问异常
end;

我还是坚持我的看法:编译函数在碰到一个类时它就已经确定了其中的静态函数的入口地址,
所有这个类的对象都共享这段代码,至于virtaul和abstract方法则是滞后联通。
 
看的一头雾水,还是要看,收藏之,学习之,消化之[8D][8D]
 
to ZXW49362727:
procedure TObject.Free;
asm
TEST EAX,EAX
JE @@exit
MOV ECX,[EAX]
MOV DL,1
CALL dword ptr [ECX].vmtDestroy
@@exit:
end;
这是Free的源码,你从哪里看出来它肯定不是静态函数?

另外,C++都有不符合OOP概念的地方,VCL不符合又有什么大关系?OOP是一种方法而不是
教条!
 
Adocnn:=Tadocnnection.Create(nil);和...Create(Application)有何区别??
顺便提个问题,如上![:(][:(]
 
原来的c++都是把c++ source code 编译成 c source code 的。类创建的时候不过是
把数据成员创建了一遍,类的代码和普通的函数没什么区别,只不过多了一个this。
虚函数也差不多:如果是虚函数,查找vmt先。实现很容易,开销也很小。
这个case中调用free成员的过程:
1.查找这个类的vmt,获取正确的调用地址(类的数据是动态的,方法地址是不变的)。
2.jump过去,当然,之前要把 this 推入堆栈。this 在这个情况下是 null。
3.free函数检查this,如果是null就return,否则销毁。

这又不是什么高深莫测的问题,怎么这么多re?工作原理只有一个,research一下不就行了?
 
[:I]to savenight,不好意思,误会了,不要见怪。

 
to sadj:你说的静态函数的概念在Delphi中叫类函数,不过我现在都搞不清楚类函数
和静态函数有什么区别?都是不需要创建就可以访问,只是类函数不能访问数据区,而
静态函数在对象创建后可以访问数据区,是不是这样?
 
sadj:
〉〉无论是free还是destroy还是其他的什么方法,甚至是abstract的方法,内存地址都有了,这里是很奇怪。
我对汇编知之甚少不敢妄加猜测,不过你跟踪出来destroy的地址,我就很是怀疑了,你能说说具体跟踪步骤吗?

ZXW49362727:
教父的这句话我也想问,你是怎么肯定free不是静态函数?

 
to:教父
看来静态方法的理解有偏差,我是按照c++和java的术语去理解的,原来delphi的不一样。
to:savenight &amp
教父
我是这样找到函数的入口地址的
var
aa: ttest;
begin
aa.dosomething;[red]//这里做断点,停下来之后按住ctrl+鼠标左键,弹出方法的地名称和地址
end;
这样看见的所有的方法都是有地址的。
 
Delphi里的static概念主要是和binding联系在一起,static method就是指early binding 方法.
 
to savenight: never talk about what you don't even know.

A static function cannot be inherited. And a static function never receives
"this" pointer. So you may see it to an ordinary function.
A static function is early binded. All the non-virtual functions are also
early binded. A virtual function's address cannot be retrived at compiling,
it's late-binded.
 
free没什么特殊,它不是虚函数,调用它不需要通过访问虚表
由于有
if self<>0 then
self.Destroy
当self为nil就是传入的对想指针为0的时候没做什么操作
所以不出错
当self<>nil的时候就会执行self.Destoy,Destroy是虚函数
是从虚表查询到入口地址的,这个虚表里面虚函数的入口
倒是对象创建的时候填进去的,不过Destroy这个函数它的
代码以及函数的地址是已知的。
比如,
var
obj:TComponent;
obj:=TButton.Create(nil);
obj是一个TButton对象,不过我们把这个变量声明为TComponent
不管是TComponent.Destry还是TControl.Destroy还是TWinControl.Destroy
还是TButton.Destroy,他们的地址都是已知的,因为它声明为虚方法
它create的时候,在实例里面的虚表填入了TButton.Destroy的地址
因此obj.Destroy是通过虚表查询到TBtton.Destroy的地址的,
而不是直接用已知的TComponent.Destroy或者TButton.Destroy(我们不知道obj是TButton对象)地址的
但是,不管如何,所有函数的入口都是已知的,我们通过虚表只是
想知道该调用哪个类的虚函数而已
delphi并没有象c++绕过虚表指定使用哪个类的函数的方法:
obj->TButton::xxxxxx
 
to sadj
我说的“非法的指针”指针不必改成nil,指的是任何非法指针
TForm(123).Free()
因为会存取虚表,所以会报告错误
TForm(0).Free()
因为不会存取虚表,所以不会报告错误
 
c++的静态函数与DELPHI的类方法一致,
只是delphi自己说的静态方法和c++的静态方法又不同
在delphi帮助查 Static methods ,可以看见,他的静态方法只是相当于c++的普通的成员函数

刚才有人提到下面的free的源码,eax传入的是this指针,或者说delphi的self指针
他是c++里面的普通的成员函数,也是delphi说的static method
procedure TObject.Free;
asm
TEST EAX,EAX
JE @@exit
MOV ECX,[EAX]
MOV DL,1
CALL dword ptr [ECX].vmtDestroy
@@exit:
end;

反正能理解就理解,不能理解也不用担心,不是人人都需要理解内部的细节
200分那么多人分,唉,到手的少了一点:)
 
顶部