inherited在消息中的作用(编译器根据inherited所在的函数,直接转换成对祖先类同名动态函数的调用,或者转换成对DefaultHandler的调用)
好奇一下。看来Object Pascal确实与Windows深入结合了。
- unit Unit1;
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls, ExtCtrls;
- const
- UM_Test = WM_USER + ;
- type
- TForm1 = class(TForm)
- Button1: TButton;
- procedure Button1Click(Sender: TObject);
- private
- { Private declarations }
- public
- { Public declarations }
- procedure MyMessage(var Msg: TMessage); message UM_Test;
- end;
- var
- Form1: TForm1;
- implementation
- {$R *.dfm}
- procedure TForm1.Button1Click(Sender: TObject);
- begin
- Perform(UM_Test, , );
- end;
- procedure TForm1.MyMessage(var Msg: TMessage);
- begin
- inherited;
- ShowMessage('Hello');
- end;
- end.
在message处理中和其他不一样的是inherited不会因为没有在祖先中找到一样的函数或者方法而将inherited失效,他会传入缺省的消息处理.
这里调用TFORM1的祖先的消息处理,由于tform和tcustomform没有这个实现UM_Test函数,所以直接调用的是tcustomform的defaulthandle.(注意这个方法是对twincontrol的override)。
但是,如果本类重载了DefaultHandler函数,就会直接调用本类的DefaultHandler函数:
- unit Unit1;
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls, ExtCtrls;
- const
- UM_Test = WM_USER + ;
- type
- TForm1 = class(TForm)
- Button1: TButton;
- procedure Button1Click(Sender: TObject);
- private
- { Private declarations }
- public
- { Public declarations }
- procedure MyMessage(var Msg: TMessage); message UM_Test;
- procedure DefaultHandler(var Message); override;
- end;
- var
- Form1: TForm1;
- implementation
- {$R *.dfm}
- procedure TForm1.Button1Click(Sender: TObject);
- begin
- Perform(UM_Test, , );
- end;
- procedure TForm1.DefaultHandler(var Message);
- begin
- with TMessage(Message) do
- begin
- if Msg = UM_Test then ShowMessage('DefaultHandler');
- end;
- inherited;
- end;
- procedure TForm1.MyMessage(var Msg: TMessage);
- begin
- inherited;
- ShowMessage('Hello');
- end;
- end.
顺便再看看这样改写的效果:
- procedure TForm1.DefaultHandler(var Message);
- begin
- with TMessage(Message) do
- begin
- if Msg = UM_Test then ShowMessage('DefaultHandler');
- if Msg = WM_SETTEXT then ShowMessage('WM_SETTEXT');
- end;
- inherited;
- end;
理论解释:
因为WndProc里面调用了Dispatch,而Dispatch实际上是尝试调用动态方法,在动态方法表中查找Index是指定的方法,如果一直找到TObject都找不到Index是这个的方法,就会调用DefaultHandler。
这里面有几个概念,动态方法表和虚方法表,不要混了。
动态方法实际上在Class中是个最大65536的函数数组。调用的时候会根据Index到这个数组中来调用。而消息方法实际就是动态方法,方法声明后面的Message实际上就是Index。
VCL之所以对消息处理效率比VC等高,就是因为这个动态方法表,实际调用的就是用消息做下标直接调用方法。最多就是找几层继承的父类。而比VC的遍历要快。
Inherited动态方法的时候就是到父类的动态方法表找Index等于本方法Index的方法。如果父类没有这个方法就再到父类的父类找。都找不到就调用DefaultHandler。
动态方法和虚方法相比优点是省内存,另外能对Message的处理比较方便。虚方法不能做到对消息的方便处理。
虚方法则比较费内存,每个派生类都要有一套虚方法表。但是被调用的时候比动态方法要快,不需要到父类去找。
理论解释2:
你恰恰理解反了,不是Inherited会调用Dispatch,而是Dispatch会调用动态方法.
消息的调用次序实际上是
WndProc->Dispatch->动态方法或者DefaultHandler方法.
TControl.WndProc的最后一句就是Dispatch.
然后Dispatch就找Index的动态方法调用.
如果找不到就调用到DefaultHandler.
如果找到,就调用动态方法.
如果动态方法里面调用了Inherited,那么实际上inherited是编译器处理的.如果在祖宗里面有对应index的方法,那么就直接编译成调用该方法.如果祖宗里面没有该index的方法编译成调用DefaultHandler(存疑).
实际上这样处理效率比运行时处理要快.
Inherited必须由编译器处理的,因为Inherited有两种含义.动态方法和虚方法的实现机制不同必然要根据方法编译成不同的代码.
====================================================================
- procedure TObject.Dispatch(var Message);
- asm
- PUSH ESI
- MOV SI,[EDX] //获取index
- OR SI,SI
- JE @@default //如果是0,到default,也就是调用DefaultHandler
- CMP SI,0C000H
- JAE @@default //如果不在合理范围内,到default,也就是调用DefaultHandler
- PUSH EAX
- MOV EAX,[EAX]
- CALL GetDynaMethod //获取动态方法
- POP EAX
- JE @@default //如果GetDynaMethod返回的是nil,到default,也就是调用DefaultHandler
- MOV ECX,ESI
- POP ESI
- JMP ECX //跳转到动态方法
- @@default:
- POP ESI
- MOV ECX,[EAX]
- JMP DWORD PTR [ECX] + VMTOFFSET TObject.DefaultHandler
- end;
- procedure GetDynaMethod;
- { function GetDynaMethod(vmt: TClass; selector: Smallint) : Pointer; }
- asm
- { -> EAX vmt of class }
- { SI dynamic method index }
- { <- ESI pointer to routine }
- { ZF = 0 if found }
- { trashes: EAX, ECX }
- PUSH EDI
- XCHG EAX,ESI
- JMP @@haveVMT
- @@outerLoop:
- MOV ESI,[ESI]
- @@haveVMT:
- MOV EDI,[ESI].vmtDynamicTable //根据vmt获取该类的动态方法表
- TEST EDI,EDI
- JE @@parent
- MOVZX ECX,word ptr [EDI]
- PUSH ECX
- ADD EDI,
- REPNE SCASW
- JE @@found //跳到found
- POP ECX
- @@parent:
- MOV ESI,[ESI].vmtParent //为空,那么找父类的
- TEST ESI,ESI //测试父类是否为空
- JNE @@outerLoop //跳出循环
- JMP @@exit //退出
- @@found:
- POP EAX
- ADD EAX,EAX
- SUB EAX,ECX { this will always clear the Z-flag ! }
- MOV ESI,[EDI+EAX*-]
- @@exit:
- POP EDI
- end;
对应的pascal代码如下
- procedure TObject.Dispatch(var Message);
- type
- //THandlerProc = procedure(Self: Pointer; var Message) { of object };
- THandlerProc = procedure(var Message) of object;
- var
- MsgID: Word;
- Addr: Pointer;
- M: THandlerProc;
- begin
- MsgID := TDispatchMessage(Message).MsgID;//消息id,也就是动态方法表的索引
- if (MsgID <> ) and (MsgID < $C000) then
- begin
- Addr := GetDynaMethod(PPointer(Self)^, MsgID);//获取class的动态方法表
- if Addr <> nil then//如果拿到了动态方法就调用
- begin
- //THandlerProc(Addr)(Self, Message)
- TMethod(M).Data := Self;
- TMethod(M).Code := Addr;
- M(Message);
- end
- else
- Self.DefaultHandler(Message);//如果找不到动态方法则调用defaultHandler
- end
- else
- Self.DefaultHandler(Message);//如果index范围不在应该在的范围内也调用DefaultHandler
- end;
- function GetDynaMethod(vmt: TClass; selector: SmallInt): Pointer;
- type
- TDynaMethodTable = record
- Count: Word;
- Selectors: array[..] of SmallInt;
- {Addrs: array[0..0] of Pointer;}
- end;
- PDynaMethodTable = ^TDynaMethodTable;
- var
- dynaTab: PDynaMethodTable;
- Parent: Pointer;
- Addrs: PPointer;
- I: Cardinal;
- begin
- while True do
- begin
- dynaTab := PPointer(PByte(vmt) + vmtDynamicTable)^;//根据vmt获取该类的动态方法表
- if dynaTab <> nil then
- begin
- for I := to dynaTab.Count - do
- if dynaTab.Selectors[I] = selector then
- begin
- Addrs := PPointer(PByte(@dynaTab.Selectors) + dynaTab.Count * SizeOf(dynaTab.Selectors[]));
- Result := PPointer(PByte(Addrs) + I * SizeOf(Pointer))^;
- Exit;//能找到则退出
- end;
- end;
- Parent := PPointer(PByte(vmt) + vmtParent)^;//找不到则找父类的
- if Parent = nil then Break;//如果父类是nil,也就是tobject了,则退出
- vmt := PPointer(Parent)^;
- end;
- Result := nil;
- end;
inherited在消息中的作用(编译器根据inherited所在的函数,直接转换成对祖先类同名动态函数的调用,或者转换成对DefaultHandler的调用)的更多相关文章
- python装饰器中@wraps作用--修复被装饰后的函数名等属性的改变
Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的de ...
- SET STATISTICS IO和SET STATISTICS TIME 在SQL Server查询性能优化中的作用
近段时间以来,一直在探究SQL Server查询性能的问题,当然也漫无目的的查找了很多资料,也从网上的大神们的文章中学到了很多,在这里,向各位大神致敬.正是受大神们无私奉献精神的影响,所以小弟也作为回 ...
- SLAM+语音机器人DIY系列:(二)ROS入门——8.理解roslaunch在大型项目中的作用
摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...
- __setup 在内核中的作用【转】
本文转载自:http://blog.csdn.net/lanmanck/article/details/7613305 本文来自: http://blog.chinaunix.net/uid-1379 ...
- [转载]java中import作用详解
[转载]java中import作用详解 来源: https://blog.csdn.net/qq_25665807/article/details/74747868 这篇博客讲的真的很清楚,这个作者很 ...
- static在c\c++中的作用(翁恺c++公开课[28-29]学习笔记)
static相对来说是一个较复杂的修饰符,c++中的static在c的基础之上又包含了static在类中的应用(也就是说多了static的成员变量和static的成员函数):c\c++中静态变量.对象 ...
- static在C/C++中的作用-(转自华山大师兄)
1.先来介绍它的第一条也是最重要的一条:隐藏.(static函数,static变量均可) 当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性.举例来说明.同时编译两个源文件 ...
- ZeroMQ接口函数之 :zmq_msg_copy - 把一个消息的内容复制到另一个消息中
ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq_msg_copy zmq_msg_copy(3) ØMQ Manual - ØMQ/3.2.5 Name zm ...
- 微软承诺将在今年的 Visual C++ 更新中加入 Clang 编译器
微软最近发布将在2015年11月 Visual C++ 更新中加入 Clang 编译器 ,Clang 开源编译器以相比GCC更快的编译速度和更优的错误提示著称. Clang关于C,C++,及Objec ...
随机推荐
- 【Luogu】P2831愤怒的小鸟(手算抛物线+状压DP)
题目链接 设f[s]表示二进制集合表示下的s集合都打掉用了多少小鸟. 预处理出lne[i][j]表示i.j确定的抛物线能打掉的小鸟集合. 于是就有f[s|lne[i][j]]=min(f[s|lne[ ...
- BZOJ 2298: [HAOI2011]problem a【动态规划】
Description 一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低.”问最少有几个人没有说真话(可能有相同的分数) Input 第一行一个整数n,接下来n行每行两个 ...
- day2之爬取拉勾网
认证流程 浏览器清空cookies 步骤一 访问拉勾网网站 https://www.lagou.com/ 做了些什么: 以get方式请求"https://www.lagou.com/&qu ...
- windows bat语法
@echo off 表示在这条语句之后,所有执行命令的语句不会显示 setLocal 参考1 rem 注释 :或者:: 参考 set /p 等待用户输入 SET /A express ...
- BZOJ——3296: [USACO2011 Open] Learning Languages
http://www.lydsy.com/JudgeOnline/problem.php?id=3296 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: ...
- Jetson TK1 二:usb无线网卡的使用
一.总体是按照群里的文档“TK1连接无线网络”的步骤操作的,但也遇到了一些问题,如下: 1.自动配置设备并下载内核源代码到指定的目录下时(估计是解压时),出现时间超前之类的问题,原因是当前本地时间是几 ...
- Object源码
1.Object是所有类的父类,默认会继承Object. 2.Object类中常用的方法有:getClass().hashCode().equals().clone().toString().fina ...
- IntelliJ IDEA插件-常用插件
IntelliJ IDEA的插件真的很多,最近的新版集成的插件已经基本够用,下面是收集的一些常用插件,根据需要来安装和测试.如果还是没有找到,那么自己来开发一个. 官网:https://plugins ...
- Java 5/Java 6/Java7/Java 8新特性收集
前言: Java 8对应的JDK版本为JDK8,而官网下载回来安装的时候,文件夹上写的是JDK1.8,同一个意思.(而这个版本命名也是有规律的,以此类推) 一.Java 5 1.https://seg ...
- Linux 开发板网络设置
改动IP地址步骤: ①改动/etc/eth0-setting 命令:vi /etc/eth0-setting ②改动对应的信息.最后:wq退出 ③重新启动eth0 命令:/etc/init.d/ifc ...