通过没有初始化的实例调用类方法,遇到怪事了。 ( 积分: 200 )

  • 主题发起人 主题发起人 LeeChange
  • 开始时间 开始时间
L

LeeChange

Unregistered / Unconfirmed
GUEST, unregistred user!
答题,人家不信有通过类调用方法一说,编代码试,结果很晕。
简单代码如下:
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TA = class
public
class procedure a;
end;

TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

{ TA }

class procedure TA.a;
begin
ShowMessage(ClassName)
end;

procedure TForm1.Button1Click(Sender: TObject);
var
a: TA;
begin
TA.a
//第一句
a.a
//第二句
end;

end.
第一句和第二句分别出什么结果?
把第一句和第二句的顺序倒一下又是什么结果?
关键一问:why?

btw:至于怎么做才正确的回答就算了,关键是想知道出这种怪异结果的原因。
 
试一下,真的想不到是这个结果。如A实例化后结果就对了。奇怪
 
看汇编,都是call ta.a
关键是装入eax的参数不同,第一句因为明确调用ta,所以取得了正确的类名。
第二局由于a没有初始化,结果传入eax的值是不确定的,可能是tbutton或者tform,或者别的东东,看语句所在的位置而定。
 
通过CPU窗口可以看出使用ta.a的时候eax被富于一个硬编码地址,我想这个应该是程序装载后ta在内存中的其实地址,所以它加上偏移可以正确寻址a,当没有a的时候如果之前执行了a:=ta的话可以看到mov eax [004601e8],call tobject.create,move eax,[eax],call ta.a,从这两句话中可以看出delphi在调用类方法时候Self使用的是硬编码的地址,即类的起始地址,而当调用了create方法后self使用的是实例的地址
 
通过CPU窗口可以看出使用ta.a的时候eax被富于一个硬编码地址,我想这个应该是程序装载后ta在内存中的其实地址,所以它加上偏移可以正确寻址a,当没有a的时候如果之前执行了a:=ta的话可以看到mov eax [004601e8],call tobject.create,move eax,[eax],call ta.a,从这两句话中可以看出delphi在调用类方法时候Self使用的是硬编码的地址,即类的起始地址,而当调用了create方法后self使用的是实例的地址
 
不用猜,对于类,如果没有初始化,则相当于这个事件的Sender。
所以a.a的结果应该是TButton。
 
做这个有什么用?
 
看来“人家”是不知道还有“类方法”一说,打开TObject看看,几乎一半的方法都是类方法。

至于实例未初始化就调用,应该是结果不可预料的,之所以有现在这样是由于编译器优化的结果,只要取消优化即可现原形。
操作方法:在Project Options->Compiler->Code generation下面去掉Optimizition前面的钩,然后Build即可。
 
楼上一语点醒啊,去掉优化后无论什么顺序确实就只剩下AV错误一条路了,看了下编译后的代码也是一致的了.
昨天私下请教了几位大侠,讨论的结果其实传入eax的值也不是完全不确定的,只是会在eax和ebx之间选择.最后问题聚焦于"编译器为什么会把同样的a.a这句话只因为位置不同而编译出不同的结果",经楼上的提醒,意识到是优化在起作用,看来从根本上解释这个问题,就得知道编译器的优化原则了,不知道有没有哪位兄台明了.

to HJ.yao:做这个没任何用,纯好奇而已.
 
后退
顶部