在子類覆蓋的虛方法中,可以用inherited呼叫父類的實現,但有時候我們並不需要父類的實現,而是想躍過父類直接呼叫祖先類的方法。
舉個例子,假設有三個類,實現如下:
type
tclassa = class
procedure proc; virtual;
end;
tclassb = class(tclassa)
procedure proc; override;
end;
tclassc = class(tclassb)
procedure proc; override;
end;
implementation
procedure tclassa.proc;
begin
showmessage('proc of class a');
end;
procedure tclassb.proc;
begin
showmessage('proc of class b');
end;
procedure tclassc.proc;
begin
showmessage('proc of class c');
end;
用如下**呼叫虛方法proc:
varc: tclassa;
begin
c := tclassc.create;
c.proc;
c.free;
end;
我們知道最終呼叫的是tclassc.proc;如果在tclassc.proc中加上inherited,則tclassb.proc可以得到呼叫;但是現在,若想在tclassc.proc中直接呼叫tclassa.proc,該怎麼辦呢?
如果是c++,只需要這樣寫:tclassc::proc。
在delphi卻沒有辦法做到,delphi不允許我們躍級呼叫祖先類的方法。儘管如此,還是能從另乙個角度來尋求解決的辦法。
解決之道就是vmt,每乙個類就是乙個指向vmt的指標,而vmt的作用其實就是用來儲存虛方法的。在vmt的正方向上,列著從祖先類起的所有虛方法,只需要偏移tclassa的vmt到proc,然後呼叫之即可。
來看看這個問題是怎麼得解決的:
procedure tclassc.proc;
type
tproc = procedure of object;
varm: tmethod;
begin
m.code := ppointer(tclassa)^;
m.data := self;
tproc(m)();
showmessage('proc of class c');
end;
執行一次呼叫,可以看到先彈出:proc of class a;然後彈出:proc of class c。這說明tclassa.proc在tclassc.proc中被呼叫到了。
請注意上面的**,tclassa的vmt上的第0偏移就是proc的位址,而tclassa繼承自tobject,tobject本身也有一些虛方法的,比如afterconstruction,那麼這些是存放在**呢?
秘密就在vmt的負偏移上,在system單元中宣告了虛表的結構偏移,在負方向上有afterconstruction的進入點。需要指出的是,system單元中宣告了結構偏移正方向的幾個已經過時了,第0偏移(vmtqueryinte***ce)不是存放queryinte***ce,而是存放第乙個虛方法(除tobject外)。
下面是從幫助上拷下來的vmt布局:
offset type description
-76 pointer pointer to virtual method table (or nil)
-72 pointer pointer to inte***ce table (or nil)
-68 pointer pointer to automation information table (or nil)
-64 pointer pointer to instance initialization table (or nil)
-60 pointer pointer to type information table (or nil)
-56 pointer pointer to field definition table (or nil)
-52 pointer pointer to method definition table (or nil)
-48 pointer pointer to dynamic method table (or nil)
-44 pointer pointer to short string containing class name
-40 cardinal instance size in bytes
-36 pointer pointer to a pointer to ancestor class (or nil)
-32 pointer pointer to entry point of safecallexception method (or nil)
-28 pointer entry point of afterconstruction method
-24 pointer entry point of beforedestruction method
-20 pointer entry point of dispatch method
-16 pointer entry point of defaulthandler method
-12 pointer entry point of newinstance method
-8 pointer entry point of freeinstance method
-4 pointer entry point of destroy destructor
0 pointer entry point of first user-defined virtual method
4 pointer entry point of second user-defined virtual method
利用虛表呼叫虛方法的做法,終究不是安全的,因為borland(codegear)沒有向你保證每乙個delphi版本的vmt布局都是一樣的。
因此,使用這個方法的時候要慎之又慎。
C 類的繼承 訪問許可權 虛繼承
今天突然發現自己關於類的繼承,虛基類,訪問許可權混在在一起的時候有有一些模糊,所以今天進行重新學習一下。如果錯誤或者不對的地方,還請指教。一 類的繼承方式 類有三種成員成員,private,protected,public,基類的成員 派生方式 子類訪問特性 public protected pri...
訪問類的方法和屬性
using system using system.reflection using system.collections.generic using system.runtime.compilerservices class example set private int instanceprop...
C 巢狀類的訪問方法
對於以下資料,如何在執行時通過字串來得程式設計客棧到靜態變數uipath的值。複製 如下 public class ga程式設計客棧memainmenu uiclass 像下面這樣即可。複製 如下 bindingflags flag bindingflags.static bindingflags....