在Delphi的帮助中:The inherited statement searches backward through the class hierarchy and invokes the first message handler with the same ID as the current method, automatically passing the message record to it. If no ancestor class implements a message handler for the given ID, inherited calls the DefaultHandler method originally defined in TObject. The implementation of DefaultHandler in TObject simply returns without performing any actions. By overriding DefaultHandler, a class can implement its own default handling of messages. The DefaultHandler method for VCL controls calls the Windows DefWindowProc function.意思应该是:当消息处理句柄调用inherited而祖先类没有定义处理句柄,则调用祖先的DefaultHandler方法,而TCurstomForm重载了此方法。导致上面的行为,看来得好好研究帮助啊!
binbin, OK 我以前也学bcb 在Delphi中也是一样阿 只不过我觉得在c++中更自由一些 没记错的话 可以调爷爷或辈更大的方法 而在delphi中只能调父亲的方法 想调爷爷的方法,我不知道用什么方法调
那就让父类再Inherited传给爷爷吧:)
关于message handler在inherited上的行为,我在Borland的新闻组上得到了更详细的答复: message handler是动态方法,类似虚函数表,Delphi有动态方法表(是不是有点象消息映射表?),在调用时会进行查找,下面是原文,希望对大家有所帮助:WMLButtonDblClk is a message handler, message handlers are dynamic methods. A call to a dynamic method works a little differently than a call to a static or virtual method. For a static method the compiler will simply code a direct call to the methods address. If you use inherited in a static method (which is legal) the compiler will look for a method of the same name and parameter signature in a base class and code a direct call to that method. So the address is resolved at compile-time.For a virtual method the compiler looks up the methods address in the classes virtual method table. This is basically an array of method addresses, each "chain" of virtual methods (the method declared virtual in the base class + all overriden versions in descendent classes) has a fixed index in the VMT, the compiler codes an indirect call to the entry in the VMT. The offset the method address can be found at in the classes VMT is calculated at compile-time but the method address is resolved at run-time. If you call inherited in an overriden virtual method the compiler simply codes an indirect call using the immediate ancestor classes VMT instead of the current objects classes VMT. Each classes VMT starts with a copy of the VMT of its ancestor class, with entries for methods that have been overriden in the class changed to point to the classes methods. For a dynamic method the picture is more complicated still. A classes dynamic methods are listed in another table that is associated with the class record, the dynamic method table. This is basically an array of key/value pairs, the key for a dynamic method is a number (the message ID for a message handler), the value is the methods address. A classes DMT contains only the dynamic methods declared in the class itself, no methods from the ancestor classes. This makes DMTs smaller than VMTs, but calling a dynamic method involves a linear search for a method matching the methods key through a classes DMT and the ancestor classes DMTs, if no match is found in the current class. The compiler will code a call to a run-time library function (_CallDynaInst) when it needs to call a dynamic method indirectly (i.e. if you use inherited in a dynamic method, or Dispatch gets called to deliver a message to the object). _CallDynaInst uses another method _FindDynaInst to perform the search for the method in the classes DMT. If it finds no method matching the key _CallDynaInst will call DefaultHandler instead. If you call inherited in a dynamic method _CallDynaInst will start the search at the immediate base class of the class you have the inherited in. For TControl that means that all inherited calls will end up calling DefaultHandler, since none of the base classes has any message handlers.
我来总结一下: 1。Inherited 关键字一般是调用父类同名方法。 这里的一般是指,方法是虚方法或者普通方法。 2。当方法为动态方法,Inherited 会调用 _CallDynaInst _CallDynaInst会调用 GetDynaMethod 线性在DMT中寻找索引一致的方法 如果没有找到,则在父类的DMT中继续查找,一直找到TObject.DefaultHandler 为止,消息处理函数就是放在DMT中,消息函数的索引就是消息索引, 其他的动态方法的索引为了避免和同一个类中的消息函数索引冲突, 一般从$FFFF向下分配,正是zwjchina(蒲石)感觉的那样,消息处理函数,编译器会特殊处理。 (这里提醒大家,自定义消息时,消息索引不要让最高位为1,即不要用大于$8000,不易产生冲突)。一句话,动态方法,或者消息处理函数内部,使用没有参数的Inherited时, 编译器翻译Inherited就是翻译成一个 _CallDynaInst调用。 而虚方法和普通方法时,编译器在编译期指定同名方法的Call调用。同意hahaxiao()的观点,delphi编译器已经和VCL紧密结合了。to binbin(滨滨,不是二进制): “在C++中没有Inherited语法,调用父类的方法必须显式调用如: TA::B(); 这种调用将使编译器放弃虚函数表机制而直接调用,因此无法达到同样的目的”呵呵,其实object pascal中这里也没有用到VMT,所以c++显式调用达到的效果是一样的 原因我上面已经分析过了。system.pas 可以跟踪进去。to xzgyb(老达摩): “binbin, OK 我以前也学bcb 在Delphi中也是一样阿 只不过我觉得在c++中更自由一些 没记错的话 可以调爷爷或辈更大的方法 而在delphi中只能调父亲的方法 想调爷爷的方法,我不知道用什么方法调”和c++中一样的, ie TObj = class TObj1 = class(TObj) TObj2 = class(TObj1) Obj2 = TObj2.Create; TObj(Obj2).P1; 这里只要type cast一下就可以了,同样可以调用任何祖先的普通方法。 如果是虚方法,或者动态方法,可以嵌入汇编,手动写查找VMT,或者DMT的代码 也一样可以实现执行任意祖先方法。Borland newsgroup这篇文章有一点点错, “_CallDynaInst uses another method _FindDynaInst to perform the search for the ~~~~~~~~~~~~~~这里并没有调用_FindDynaInst,而是调用了GetDynaMethod。
method in the classes DMT. If it finds no method matching the key _CallDynaInst will call DefaultHandler instead.” //粗略的写了一下,欢迎大家继续讨论。
推荐几本书把,讲底层的: Developing Custom Delphi3 Components 2nd.Ed (很多国外的delphi牛人喜欢这本书,在他们的书架上都能看到,黄色封皮) Delphi in a Nutshell by Ray Lishner Secret of Delphi2 by Ray Lishner Delphi Developer's Handbook (delphi高级开发指南) OPLG(Object Pascal Language Guides),呵呵这本书才是权威,也很有深度,呵呵,delphi光盘中自带, 不知大家注意过没有。btw:csdn上很少看到这种有价值的问题
很感谢nofog的透彻分析,多谢 希望继续讨论
补充: 3。inherited,后面是个函数方法的话,不能省掉参数。inherited fun(...);//correctinherited; //wrong4。inherited 所要执行的方法是抽象的,则会引起运行时异常。这里是 Peter Below写的一个手动查找DMT的函数 FindDynamicMethod(aClass.ClassGrandparent, MessageID);用一个TMethod类接收返回值 可以直接执行祖父消息处理函数(可以屏蔽父亲的处理)function FindDynamicMethod(aClass: TClass; anIndex: SmallInt): Pointer; type TIndices= Array [1..1024] of SmallInt; TProcs = Array [1..1024] of Pointer; var pDMT : PWord; i, count: Word; pIndices : ^TIndices; pProcs : ^TProcs; begin Result := nil; if aClass = nil then Exit; pDMT := Pointer(aClass); // Find pointer to DMT in VMT, first Word is the count of dynamic // methods pDMT := Pointer(PDword(Integer(pDMT) + vmtDynamicTable)^); count := pDMT^; pIndices := Pointer(Integer(pDMT) + 2 ); pProcs := Pointer(Integer(pDMT) + 2 + count * sizeof(smallint)); // find handler for anIndex for i:= 1 to count do if pIndices^[i] = anIndex then begin Result := pProcs^[i]; Break; end; if Result = nil then Result := FindDynamicMethod(aClass.Classparent, anIndex); end;下面给用BCB的朋友提个问题,呵呵,大家继续讨论。 c++中没有 inherited ,如果父类方法是个private 子类中改如何实现调用父类的同名方法。 比如消息处理的时候, void __fastcall TMyEdit::WMChar(TWMKey& Message) { //想要调用父类 TEdit::WMChar,How to? }
inherited执行原本应该执行的TWMLButtonDblClk消息响应过程。
//begin
push ebp
mov ebp,esp
add esp, -$08
push esi
mov [ebp - $08], edx //保存的Message地址
mov [ebp - $04], eax //保存的Self值//SendCancelMode(self)
mov edx, [ebp - $04]
mov eax, [ebp - $04]
call TControl.SendCancelMode// inheritedmov edx, [ebp - $08] //保存的Message地址
mov eax, [ebp - $04] //取得Self
mov ecx, [eax] //取得vptr
call dword ptr [ecx - $10] //-$10在System中正好是 vmtDefaultHandler = -16;也就是调用DefaultHandler,我用TButton跟踪进入了TWinControl的DefaultHandler
估计是一个消息如果在祖先中没有对应的响应,编译器就认为Inherit就调用了DefaultHandler
另外要跟踪进源码
设一下Project\options\compiler的Use Debug DCUs选上就可
WMLButtonDblClk
inherited;调用的结果是:
procedure TCustomForm.DefaultHandler(var Message);
我实在是想不通!!!
Borland不会为了一个消息处理安排这么一个语言特例吧?难道不能用标准方法来实现吗?
看其声明过程 procedure FunctionName();Message WM_MEssage;
^^^^^^^
其他过程是没有Message的!
另外VC和BCB中对消息的处理都是通过消息映射!由此可看出程序对消息处理
有一定的特殊性!具体是怎么会这样我也不清楚,只是谈点我的看法!
当然在特殊的情况下,可以是你的代码优先,甚至可以接管这个消息处理(当然不推荐,除非你认为非常安全)。我的一点理解,但愿对你有用。
因为TCustomForm重载了DefaultHandler
procedure DefaultHandler(var Message); virtual;
在TObject中 procedure DefaultHandler(var Message); virtual;
这很正常阿
消息处理方法本来就是个特例
看其声明过程 procedure FunctionName();Message WM_MEssage;
^^^^^^^这倒并不奇怪,这里是为Dispatch方法准备映射数据,而我要探讨的是继承,为什么一个inherited能有这样的效果,你做几个继承的类出来看看,能不能由一个祖先的方法调用inherited就能调用到后代的某个方法上?这与上面的问题好象没有关系。
The implementation of DefaultHandler in TObject simply returns without performing any actions. By overriding DefaultHandler, a class can implement its own default handling of messages. The DefaultHandler method for VCL controls calls the Windows DefWindowProc function.意思应该是:当消息处理句柄调用inherited而祖先类没有定义处理句柄,则调用祖先的DefaultHandler方法,而TCurstomForm重载了此方法。导致上面的行为,看来得好好研究帮助啊!
TA = class
procedure A; virtual;
procedure B; virtual;
end;TB = class(TA)
procedure A; override;
procedure B; override;
end;procedure TA.A;
begin
B;
end;procedure TA.B;
begin
//
end;procedure TB.A;
begin
Inherited;
end;procedure TB.B;
begin
ShowMessage('TB.B');
end;
你的例子我理解,其实我是用C++Builder的,多态在C++是用虚函数实现的。我提出的问题主要针对Inherited,现在明白了。
在C++中没有Inherited语法,调用父类的方法必须显式调用如:
TA::B();
这种调用将使编译器放弃虚函数表机制而直接调用,因此无法达到同样的目的,C++中只要直接调用
B();
即可,因为如果子类重载了该方法,虚函数表中的指针已经指向了子类的方法。
我以前也学bcb
在Delphi中也是一样阿
只不过我觉得在c++中更自由一些
没记错的话
可以调爷爷或辈更大的方法
而在delphi中只能调父亲的方法
想调爷爷的方法,我不知道用什么方法调
message handler是动态方法,类似虚函数表,Delphi有动态方法表(是不是有点象消息映射表?),在调用时会进行查找,下面是原文,希望对大家有所帮助:WMLButtonDblClk is a message handler, message handlers are dynamic methods. A
call to a dynamic method works a little differently than a call to a static
or virtual method. For a static method the compiler will simply code a direct
call to the methods address. If you use inherited in a static method (which
is legal) the compiler will look for a method of the same name and parameter
signature in a base class and code a direct call to that method. So the
address is resolved at compile-time.For a virtual method the compiler looks up the methods address in the classes
virtual method table. This is basically an array of method addresses, each
"chain" of virtual methods (the method declared virtual in the base class +
all overriden versions in descendent classes) has a fixed index in the VMT,
the compiler codes an indirect call to the entry in the VMT. The offset the
method address can be found at in the classes VMT is calculated at
compile-time but the method address is resolved at run-time. If you call
inherited in an overriden virtual method the compiler simply codes an
indirect call using the immediate ancestor classes VMT instead of the current
objects classes VMT. Each classes VMT starts with a copy of the VMT of its
ancestor class, with entries for methods that have been overriden in the
class changed to point to the classes methods. For a dynamic method the picture is more complicated still. A classes dynamic
methods are listed in another table that is associated with the class record,
the dynamic method table. This is basically an array of key/value pairs, the
key for a dynamic method is a number (the message ID for a message handler),
the value is the methods address. A classes DMT contains only the dynamic
methods declared in the class itself, no methods from the ancestor classes.
This makes DMTs smaller than VMTs, but calling a dynamic method involves a
linear search for a method matching the methods key through a classes DMT and
the ancestor classes DMTs, if no match is found in the current class. The
compiler will code a call to a run-time library function (_CallDynaInst) when
it needs to call a dynamic method indirectly (i.e. if you use inherited in a
dynamic method, or Dispatch gets called to deliver a message to the object).
_CallDynaInst uses another method _FindDynaInst to perform the search for the
method in the classes DMT. If it finds no method matching the key
_CallDynaInst will call DefaultHandler instead. If you call inherited in a dynamic method _CallDynaInst will start the search
at the immediate base class of the class you have the inherited in. For
TControl that means that all inherited calls will end up calling
DefaultHandler, since none of the base classes has any message handlers.
1。Inherited 关键字一般是调用父类同名方法。
这里的一般是指,方法是虚方法或者普通方法。
2。当方法为动态方法,Inherited 会调用 _CallDynaInst
_CallDynaInst会调用 GetDynaMethod 线性在DMT中寻找索引一致的方法
如果没有找到,则在父类的DMT中继续查找,一直找到TObject.DefaultHandler
为止,消息处理函数就是放在DMT中,消息函数的索引就是消息索引,
其他的动态方法的索引为了避免和同一个类中的消息函数索引冲突,
一般从$FFFF向下分配,正是zwjchina(蒲石)感觉的那样,消息处理函数,编译器会特殊处理。
(这里提醒大家,自定义消息时,消息索引不要让最高位为1,即不要用大于$8000,不易产生冲突)。一句话,动态方法,或者消息处理函数内部,使用没有参数的Inherited时,
编译器翻译Inherited就是翻译成一个 _CallDynaInst调用。
而虚方法和普通方法时,编译器在编译期指定同名方法的Call调用。同意hahaxiao()的观点,delphi编译器已经和VCL紧密结合了。to binbin(滨滨,不是二进制):
“在C++中没有Inherited语法,调用父类的方法必须显式调用如:
TA::B();
这种调用将使编译器放弃虚函数表机制而直接调用,因此无法达到同样的目的”呵呵,其实object pascal中这里也没有用到VMT,所以c++显式调用达到的效果是一样的
原因我上面已经分析过了。system.pas 可以跟踪进去。to xzgyb(老达摩):
“binbin, OK
我以前也学bcb
在Delphi中也是一样阿
只不过我觉得在c++中更自由一些
没记错的话
可以调爷爷或辈更大的方法
而在delphi中只能调父亲的方法
想调爷爷的方法,我不知道用什么方法调”和c++中一样的,
ie TObj = class
TObj1 = class(TObj)
TObj2 = class(TObj1)
Obj2 = TObj2.Create;
TObj(Obj2).P1;
这里只要type cast一下就可以了,同样可以调用任何祖先的普通方法。
如果是虚方法,或者动态方法,可以嵌入汇编,手动写查找VMT,或者DMT的代码
也一样可以实现执行任意祖先方法。Borland newsgroup这篇文章有一点点错,
“_CallDynaInst uses another method _FindDynaInst to perform the search for the
~~~~~~~~~~~~~~这里并没有调用_FindDynaInst,而是调用了GetDynaMethod。
method in the classes DMT. If it finds no method matching the key
_CallDynaInst will call DefaultHandler instead.” //粗略的写了一下,欢迎大家继续讨论。
Developing Custom Delphi3 Components 2nd.Ed
(很多国外的delphi牛人喜欢这本书,在他们的书架上都能看到,黄色封皮)
Delphi in a Nutshell by Ray Lishner
Secret of Delphi2 by Ray Lishner
Delphi Developer's Handbook (delphi高级开发指南)
OPLG(Object Pascal Language Guides),呵呵这本书才是权威,也很有深度,呵呵,delphi光盘中自带,
不知大家注意过没有。btw:csdn上很少看到这种有价值的问题
希望继续讨论
3。inherited,后面是个函数方法的话,不能省掉参数。inherited fun(...);//correctinherited; //wrong4。inherited 所要执行的方法是抽象的,则会引起运行时异常。这里是 Peter Below写的一个手动查找DMT的函数
FindDynamicMethod(aClass.ClassGrandparent, MessageID);用一个TMethod类接收返回值
可以直接执行祖父消息处理函数(可以屏蔽父亲的处理)function FindDynamicMethod(aClass: TClass; anIndex: SmallInt): Pointer;
type
TIndices= Array [1..1024] of SmallInt;
TProcs = Array [1..1024] of Pointer;
var
pDMT : PWord;
i, count: Word;
pIndices : ^TIndices;
pProcs : ^TProcs;
begin
Result := nil;
if aClass = nil then
Exit;
pDMT := Pointer(aClass);
// Find pointer to DMT in VMT, first Word is the count of dynamic
// methods
pDMT := Pointer(PDword(Integer(pDMT) + vmtDynamicTable)^);
count := pDMT^;
pIndices := Pointer(Integer(pDMT) + 2 );
pProcs := Pointer(Integer(pDMT) + 2 + count * sizeof(smallint));
// find handler for anIndex
for i:= 1 to count do
if pIndices^[i] = anIndex then
begin
Result := pProcs^[i];
Break;
end;
if Result = nil then
Result := FindDynamicMethod(aClass.Classparent, anIndex);
end;下面给用BCB的朋友提个问题,呵呵,大家继续讨论。
c++中没有 inherited ,如果父类方法是个private
子类中改如何实现调用父类的同名方法。
比如消息处理的时候,
void __fastcall TMyEdit::WMChar(TWMKey& Message)
{
//想要调用父类 TEdit::WMChar,How to?
}
谢谢nofog的补充
void __fastcall TMyEdit::WMChar(TWMKey& Message)
{
//想要调用父类 TEdit::WMChar,How to?
}
试试在说
呵呵
没有仔细斟酌,调试的时候也没有看清楚,结果错误百出。惭愧啊,惭愧。
多亏xzgyb(老达摩)指出,感激不尽。的确,我把消息处理和动态方法搞错了。
消息处理有单独的查找DMT的函数(Dispatch),
动态方法也是和普通方法一样,生成一个同名方法的call。这里我做一个猜测:message关键字声明的方法,编译器会把他加到当前
类的DMT中。我没有时间验证了(12个小时后,我就坐火车回家了)。
留给大家。至于inherited执行的是个abstract方法,引起异常,这已经涉及到了
delphi复杂的异常处理机制了。暂时放一下了。to xzgyb(老达摩):多谢,我们交个朋友把 :)
交个朋友,求之不得了
呵呵
可能现在一回家了吧
今天是小年了,在这里也祝所有的兄弟小年快乐
呵呵