写此文的目的是分为了自己彻底弄清方法指针,编译器是怎样来实现的,同时希望对别人也有用
一个方法都有地址,所以我们希望有一个方法来获得这个地址,对此,编译器实现了这一目的,通常一个普通方法指针是这样来赋值的
Type
TSimpleFunc=Procedure(i:integer) //声明方法类型
var
aSimpleFunc:TSimplefunc;
aSimpleFunc:=BFunc ;//赋值一个实体方法
这样就完成了一个普通方法的指针定义和赋值,调用就可以用这个方法指针来调用;
在对比了对象方法指针的赋值以后,我觉量从理解的角度出发,把这个变法指针称为方法变量还恰当一些,因为它没有我们一般意义上指针的^定义符号,避免与一般指针搞混,说它为指针也完全是一个约定的习惯而已.
当编译器看到
var aSimpleFunc:TSimpleFunc ; aSimpleFunc:=BFunc 发生了什么呢,?它给这个方法变量分配4字节的内存,并往这个内存中写入了BFunc方法的4字节的地址,
对象方法指针与一般方法指针不同
Type
TObjectFunc=Procedure(i:integer) of Object //声明对象方法类型要加上of Object
var
aObjectFunc:TObjectfunc;
...........(TTest)类定义,及方法定义省略)
aObjectFunc:=aTest.func //赋予一个实体对象的方法地址;(var aTest:TTest)
当编译器看到以上两行时,又发生了什么呢?
编译器对aObjectFunc这个变量分配了8个字节的内存,第1个4个字节,填入了aTest.Func这个方法的地址(其实是TTest.func的地址, 这是函数体实现的地方,只有它经过编译后才有地址),后4个字节,填充了对象aObject的地址.这就是对象方法变量的的全部内容;看上去很简单,但它包含了一个重要的事实就是,对象方法必须要包括对象的地址,因为对象方法内要调用对象的数据成员或其它与对象有关的东西,你没有对象地址什么都白搭.普通方法内部就不需要调用啥对象的东西,所以它只需要一个方法地址就ok
所以以后看到对象方指针就不要把它看成是一个一般意义上的指针东西,而是要把它看成是一个结构体的变量.一个成员是方法地址,一个成员是对象地址,这就是Delphi TMethod的定义,通过TMethdo强制类型转换就可以分离出对象方法变量的这两部分内容;
对象方法指针的调用:aObjectFunc(参数1...参数2..)这样就行,delphi编译器会自动分离出对象当作隐含参数传入,再call 对象方法,(可以看一下cpu汇编码);
值得一提的是: @aObjectFunc 得到的是方法地址,@@aObjectFunc这是变量本身的地址,这和我们常见的取变量地址的意义不同,很难从字面上解释清楚,记住就行.
我们还可以从delphi的源码当中看到这样的定义(TApplication.HookMainWindow)
var
PObjectFunc:^TObjectFunc
初看上去,看不明白这是啥意思,有点绕.这无疑是一个真正指针,符合我们心中对指针的定义,那这个指针能保存什么样的地址呢?
慢慢想一想才能明白,任何指针都只能保存一个4字节的地址,最常见的就是指向一个已有变量的地址,要么就自已分配一个内存得到一个地址,
那么用它来指向上文所说的对象方法变量的地址正恰当.
PobjectFunc:=@@aObjectFunc ;aObjectFunc是一个对象方法变量,两个@@正是这个变量的地址.这就是对象方法变量的指针;
你不能直接将对象方法直接赋给它
PObjectFunc:=aTest.Func//错误的,
PobjectFunc是一个指针,保存的是对象方法变量的地址,通过该地址可以找到变量,而变量里面保存的有对象地址,对象方法地址
通过该指针调用对象方法是这样调用的:
TObjecFunc(PObjectFunc^) (参数....);
我所理解的关于对象方法指针(我习惯于叫对象方法变量)就是如此,水平有限,欢迎指正