delphi类消息的处理机制
文章不涉及windows消息循环和更复杂的技术,只从delphi类继承及类自身的消息处理分析,是自己的学习心得和体验.
1.搜索类本身有没有处理该消息值的函数,如果有,则终止循环.循环次数:1
2.搜索父类有没有处理该消息值的函数,如果有,则终止循环. 循环次数 2
3.向父类继续搜索,直到object
4.搜索类本身在没有默认处理函数,如果有,则终止循环
5.搜索父类有没有默认处理函数
6.向父类继续搜索.直到object.(默认处理函数,object一定有)
其实,4,5,6步骤在代码实现时,只查找了一次,这要归功于vmt(虚函数表),其简化后的vmt表如下(此图仅说明原理,并不表示编译器一定也是如此实现)
一般而言,虚函数dispatch是不会重载的,在delphi源代码中,它的实现体是汇编语言,重载这个函数没有什么实际意义了.)当然,有特殊需要或高手表演时就可以重载的.而虚函数defaulthandler是容易重载的,在Tobject类中,该方法是一个空操作,在其继承类中,可以重载.
转到原题:如果某一类如Tmyclass类,它与Tobject之间可以有任意多个父子类,当用Tmyclass.dispatch调用时,实际是调用的Tobject.dispatch函数,dispatch首先调用另一函数GetDynaMethod,它查询动态方法表(dmt)中是否有消息值等于传递消息结构中第一个成员的值,如果有,就调用相应的方法,如果没有,则父类的动态方法表继续查找.直到找到相应处理过程或Tobject的动态方法表为nil时
注:我认为,在delphi中的消息过程,本身就是一个动态方法.其内存地址写入动态方法表中,如果不是这样,怎么会从dmt中查找呢,这只是个人推测.
当然,如果用getdynalmethod方法查找不到相应的处理方法时,dispatch则会调用defaulthandler函数,这时用的是虚函数表vbt,一次操作就能马上找到了.
这个消息过程,本身也体现了,vmt和dmt的差别了.vmt执行函数快捷,省时,单占内存.而dmt执行函数需要递归查询,效率低些.
下面是引用Nicrosoft申旻的内容:
比较一下 VMT 和 DMT 的区别:
VMT 中的虚函数非常齐全,因此对每个虚函数的入口地址只需要简单的 [vptr + n] 的运算即可得到,但是 VMT 容易消耗内存(有冗余)。而 DMT 比较节省空间,但要定位到没有被覆盖的函数的入口地址时,将非常耗费时间。
一般情况下,几乎每个子类都要覆盖的函数/方法,就将它声明为 virtual;如果类层次很深,或子类很多,但某个函数/方法只被很少的子类覆盖,就将它声明为 dynamic。当然,具体就需要自己把握来选择了。