Delphi的对象机制浅探(转)


======================================================
注:本文源代码点此下载
======================================================

前几天开始阅读 vcl 源代码,可是几个基类的继承代码把我看得头大。在大富翁请教了几位仁兄后,我还是对delphi对象的创建和方法调用原理不太清楚。最后只好临时啃了一下汇编,把delphi对象操作的几个关键的方法勘察了一遍。

你可以通过以下链接知道我为什么要做这件事:

http://www.delphibbs.com/delphibbs/dispq.asp?lid=2385681

这是我花费一个晚上的测试结果,更多的细节只能以后在学习中再去了解。

主要测试项目为:

⊙ 测试目标:查看 tobject.create 的编译器实现

⊙ 测试目标:查看 constructor 函数中 inherited 的编译器实现

⊙ 测试目标:以 object reference 和 class reference 调用构造函数的编译器实现

⊙ 测试目标:考查 object 和 class 在调用 class method 时的编译器实现

⊙ 测试目标:考查 shortstring 返回值类型的函数没有赋值时编译器的实现

我把测试的细节记录在后文,一是自己留作参考,二是给对此有兴趣的朋友参考。其实更重要的是,大家可以帮忙检查我的分析有没有错误。我一直是用 delphi 的组件拖放编程,真正的功底只是这几天阅读 object pascal reference 和 vcl 得来的,汇编更是临时抱佛脚,所以错误难免。我清楚自己的水平,所以写下结论后非常担心。尽管如此,我的目的是为了学习,希望你发现错误后帮我指出来。

主要的结论是:

(*) tobject.create确实是个空函数,borland 并没有隐藏 tobject.create 的代码。tobject.create的汇编代码是由 constructor directive 指示编译器形成的,编译器对每个class 都一视同仁。

(*) dl 和 eax 是 constructor create 实现的关键寄存器。borland 将对象的创建过程设计得精妙而清晰(个人感觉,因为我不知道其他的语言比如c++是如何实现的)。

(*) 一个对象的正常的创建(obj := tmyclass.create)过程是这样的:

1. 编译器保证第一个 constructor 调用之前 dl = 1

编译器保证 inherited create 调用之前 dl = 0

2. dl = 1 时 编译器保证 create 时 eax = pointer to class vmt

dl = 0 时 编译器保证 create 时 eax = pointer to current object

3. 编译器保证任何层次的 constructor 调用后 eax = pointer to current object

4. dl = 1 时 编译器保证 create 调用 system._classcreate,并与 constructor 相同的方式使用 eax

dl = 1 时 编译器保证 create 调用 system._afterconstruction,并且调用前后 eax = pointer to current object

dl = 0 时 编译器保证 create 不会调用 system._classcreate

dl = 0 时 编译器保证 create 不会调用 system._afterconstruction

5. system._classcreate 中设置结构化异常处理,在 create 即将结束时关闭结构化异常处理。

如果出错则会(1)释放由编译器分配的内存(2)恢复堆栈至创建对象之前(3)调用 tsomeclass.destroy。

(*) object reference 方式的 constructor 调用,编译器尝试实现为 inherited 调用,结果当然是错误。

(*) class method 的调用隐含参数 eax 为指向 vmt 的指针,不管是用 class 还是 object 方式调用,编译器都会正确地把指向 class vmt 的指针传递给 eax。

要读懂下文的测试过程,可能需要相关基础,推荐阅读 object pascal reference 以下章节:

parameter passing

function results

calling conventions (register缺省调用约定,constructor 和 destructor 函数必须采用 register 约定)

inline assambly code

《delphi的原子世界》非常值得一读。

以下是测试内容:

=================================================

⊙ 测试目标:查看 tobject.create 的编译器实现

=================================================

⊙ 测试代码及反汇编代码:

procedure test; register;

var

obj: tobject;

begin

push ebp// 前2句用于设置堆栈指针

mov ebp, esp

push ecx//保存 ecx (无用的语句)

obj := tobject.create;

mov dl, $01//设置 dl = 1,通知 tobject.create 这是一次新建对象的调用

mov eax, [$004010a0]// 把指向 tobject class vmt 的指针存入 eax,

//作为 tobject.create 隐含的 self 参数

call tobject.create// 调用 tobject.create 函数

mov [ebp-$04], eax// tobject.create 返回新建对象的指针至 obj

end;

pop ecx// 恢复堆栈并返回

pop ebp

ret

⊙ tobject.create 的反汇编代码:

// 函数进入时 eax = pointer to vmt(dl = 1)

eax = pointer to instance(dl = 0)

// 函数返回时 eax = pointer to instance

test dl, dl// 检查 dl 是否 = 0

jz +$08// dl = 0则跳至 @@1

add esp, -$10// 增加 16 字节的堆栈,每次调用 _classcreate 之前都会进行

// 用于 system._classcreate 设置结构化异常处理

call @classcreate// 调用 system._classcreate

@@1:

test dl, dl// 检查 dl 是否 = 0

jz +$0f// dl = 0则跳到 end 结束过程

call @afterconstruction// dleax = pointer to vmt}

{eax vmt}

{edx pointer to result string}

pushesi

pushedi

movedi,edx// edx 是返回值串的指针

movesi,[eax].vmtclassname

xorecx,ecx

movcl,[esi]// 设置 result string 的 length

incecx

repmovsb

popedi

popesi

end;

{$endif}

结论:这只是我想了解字符串返回值的传递方式。

===================

(完)


======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值