VCL对象内存框架的疑惑(100分)

  • 主题发起人 主题发起人 hospitaltnt2
  • 开始时间 开始时间
H

hospitaltnt2

Unregistered / Unconfirmed
GUEST, unregistred user!
小弟近日写一练习程序,Form上有TEdit和TMemo控件若干,想在某控件获得焦点和失去焦点时变色,于是有:

procedure Tfrmmain.NREnter(Sender: TObject);
begin
TCustomEdit(Sender).Color:=clMoneyGreen;
end;

procedure Tfrmmain.NRExit(Sender: TObject);
begin
TCustomEdit(Sender).Color:=clWhite;
end;

将所有TEdit和TMemo控件的OnEnter和OnExit事件属性挂接。

其中,TEdit和TMemo均继承于TCustomEdit,故将Sender转换为TCustomEdit,Ctrl+F9出错。

原来TCustomEdit并无Color属性,哪能办呢,试着将TCustomEdit改为TEdit,即
...
TEdit(Sender).Color:=clMoneyGreen;
...
TEdit(Sender).Color:=clWhite;
...

编译通过且运行无故障,于是不解,难道TEdit(Sender)没有改变Sender对象的内存框架?在焦点进入TMemo控件时,我可是期待着
内存出错的,但是没有,何故?请高手解惑。
 
高手呢?富翁呢?
 
那是因为Edit和Memo都有Color属性的关系吧。
要知道,本质上第二个Sender还是Memo对象,所以它不冲突。
InsideVCL的第二章里有专门讲这个问题。
所谓的类型转换,只是调整了执行框架的范围。
 
我的理解:

所有VCL对象的属性、方法无非就是VMT的地址,而类的继承关系也不过是对VMT的扩展,调用某个类的某个属性、方法,其实就是到VMT中找到对应的入口地址读写数据或执行代码,这样,同个类的任何实例(对象),其同名的属性、方法,相对于实例的起始地址都是一样的。而对于从父类继承而来的属性、方法,子类无须修改其地址,对象引擎(且这么叫)会自动先按照父类的结构构造实例的内存结构,也就是说,所有从同一祖先类扩展来的类,其从同一祖先类继承来的属性、方法相对于实例的起始地址,是一样的。

结合这两点,那么楼主的问题就很简单了:
Color是TControl的protected属性,只要是TControl的子类,都可访问这个属性,不过很多子类并没有对外公开这个属性,比如TCustomEdit,不过,没关系,基于上面的理解,不论子类是否公开了这个属性,它都是客观存在的,并且,地址是固定的,那么,我用任何一个从TControl继承而来的公开了Color属性的子类,进行强制类型转换,就可获得这个属性的地址,然后修改,ok,你不公开属性、方法,我也一样可以访问无所谓,呵呵。

再说下我对强制类型转换的理解:还是VMT,分配出去的内存都是有格式的数据,其格式就是VMT,一个类定义,转换到最后无非就是VMT,每个类,就是一张表(VMT),强制类型转换,就是用一张表去套一块已经分配的内存,如果套错了,就会出错,如果套对了,就。。。没事。

那么,结合楼主的问题和我前面所说,有些强制类型转换明显是错误的,为什么仍然可以这么做呢?比如TLabel(Button1).OnClick,TLabel和TButton的继承线路都不同为什么这样不会出错?就是因为,OnClick是它们共同祖先的属性,无论TLabel还是TButton的OnClick,它相对于实例的起始地址是一样的,那么用TLabel的VMT去套TButton,访问OnClick属性,当然是对号入坐不会出错了,当然,如果你相试试TLabel(Button1).Canvas这样的代码,我也不反对,不过不会有什么好结果,道理很简单。

总结:
世界是简单的--只要你看到本质。

前面提到“对象引擎”(当然,我也不知道这个学名是什么),你了解了VMT在OOP中的作用,那么构造一个自己的“对象引擎”也不是不可能,原理都是一样的。

呵呵,不好意思,我虚度几年编程时光,水平实在菜的很,这个只是自己在这几年编程中的理解,很可能根本就是谬论,只是恰好能解答楼主的这个问题,在高手林立的dfw,实在是有班门弄斧的嫌疑(汗一个),也是第一次在这里发表自己对编程的看法,如果错了,希望大家善意的嘲笑一番就可以了(下次不敢了),顺便指正一下,谢谢。
 
hi,我又回来了,刚才打了这么多字,顺便回来推销下自己的问题,很简单的,知道的帮忙回答一下,都打这么多字了,没有功劳也有苦劳,借楼主的光,看看我的问题。
 
将TEdit(Sender).Color:=clMoneyGreen;加入断点,然后进入CPU(汇编)模式
Ctrl+Alt+C,会发现类似这样的汇编代码:
Unit1.pas.34: TEdit(Sender).Color:=clMoneyGreen;
004551B4 8BC2 mov eax,edx
004551B6 BAC0DCC000 mov edx,$00c0dcc0
004551BB E8241FFEFF call TControl.SetColor
Unit1.pas.35: end;
004551C0 C3 ret
很明显的静态(普通)方法调用,调用的是TControl的SetColor,实际上的效果等于
TControl(Sender).SetColor(clMoneyGreen);
//同样改成 TMemo(Sender).Text := clMoneyGreen;也一样

TEdit 和 TMemo 的Color属性都 来自SetColor,所以这样的转换会正常工作;如果不是的话,就可能 内存CRUSH
 
一个对象创建后的内存布局是固定,用不同的类型指针指向它对它的内存布局没有
任何影响,如果访问的接口变化了而已.
 
因为 Tedit 和tmemo都有color属性. 否则肯定会出错的.
 
谢谢,散分
 

Similar threads

D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
后退
顶部