標籤:sel tde lease htm 而在 win 彙編 通用 except
http://www.cnblogs.com/little-mat/articles/2206627.html
TObject是所有對象的基本類,DELPHI中的任何對象都是一個指標,這個指標指明該對象在記憶體中所佔據的一塊空間!
對象空間的頭4個位元組是指向該對象類的虛方法地址表(VMT-Vritual Method Table)。接下來的空間就是儲存物件本身成員資料的空間,並按從該對象最原始祖先類的資料成員到該對象類的資料成員的總順序,和每一級類中資料成員的定義順序儲存。
類的虛方法地址表(VMT)儲存從該類的原始祖先類派生到該類的所有類的虛方法的過程地址。
即使,我們自己並未定義任何類的虛方法,但該類的對象仍然存在指向虛方法地址表的指標,只是地址項的長度為零。可是,在TObject中定義的那些虛方法,如Destroy、FreeInstance等等,又儲存在什麼地方呢?原來,他們的方法地址儲存在相對VMT指標負方向位移的空間中。其實,在VMT表的負方向位移76個位元組的資料空間是對象類的系統資料結構,這些資料結構是與編譯器相關的,並且在將來的DELPHI版本中有可能被改變。
VMT是一個從負位移地址空間開始的資料結構,負位移資料區是VMT的系統資料區,VMT的正位移資料是使用者資料區(自訂的虛方法地址表)。TObject中定義的有關類資訊或對象運行時刻資訊的函數和過程,一般都與VMT的系統資料有關,事實上,self之上就是RTTI資訊。
一個VMT資料就代表一個類,其實VMT就是類!在Object Pascal中我們用TObject、TComponent等等標識符表示類,它們在DELPHI的內部實現為各自的VMT資料。而用class of保留字定義的類的類型,實際就是指向相關VMT資料的指標。
對我們的應用程式來說,VMT資料是靜態資料,當編譯器編譯完成我們的應用程式之後,這些資料資訊已經確定並已初始化。我們編寫的程式語句可訪問VMT相關的資訊,獲得諸如對象的尺寸、類名或運行時刻的屬性資料等等資訊,或者調用虛方法或讀取方法的名稱與地址等等操作。當一個對象產生時,系統會為該對象分配一塊記憶體空間,並將該對象與相關的類聯絡起來,於是,在為對象分配的資料空間中的頭4個位元組,就成為指向類VMT資料的指標。
//建立一個對象 obj := TObject.Create;
1) 用 TObject 對應的 VMT 為依據,調用 TObject 的 Create 建構函式。
2) 而在 Create 建構函式調用了系統的 _ClassCreate 過程,
3) 系統的 ClassCreate 過程又通過類 VMT 調用 NewInstance 虛方法。
4) 調用 NewInstance 方法的目的是要建立對象的執行個體空間,因為我們沒有重載該方法,所以,
它就是 TObject 類的 NewInstance。
5) TObjec 類的 NewInstance 方法將根據編譯器在VMT表中初始化的對象執行個體尺寸(InstanceSize),
調用 GetMem 過程為該對象分配記憶體,
6) 然後調用 InitInstance 方法將分配的空間初始化。
7) InitInstance方法首先將對象空間的頭4個位元組初始化為指向對象類對應VMT的指標,然後將其餘的空間清零。
8) 建立對象執行個體之後,還調用了一個虛方法AfterConstruction。
9) 最後,將對象執行個體資料的地址指標儲存到AnObject變數中,這樣,obj 對象就誕生了。
//消滅一個對象 Obj.Destroy;
TObject的解構函式Destroy被聲明為虛方法,它也是系統固有的虛方法之一。
1) Destory方法首先調用了 BeforeDestruction 虛方法,
2) 然後調用系統的 _ClassDestroy過程。
3) _ClassDestory 過程又通過類VMT調用 FreeInstance 虛方法,
4) 由FreeInstance方法調用FreeMem過程釋放對象的記憶體空間。
就這樣,一個對象就在系統中消失。在對象的構造和析構過程中,調用了NewInstance和FreeInstance兩個虛函數,來建立和釋放對象執行個體的記憶體空間。之所以將這兩個函式宣告為虛函數,是為了能讓使用者在編寫需要使用者自己管理記憶體的特殊對象類時(如在一些特殊的工業控製程序中),有擴充的空間。
而將AfterConstruction和BeforeDestruction聲明為虛函數,也是為了將來派生的類在產生對象之後,有機會讓新誕生的對象呼吸第一口新鮮空氣,而在對象消亡之前可以允許對象完成善後事宜,這都是合情合理的事。其實,TForm對象和TDataModule對象的OnCreate事件和OnDestroy事件,就是在TForm和TDataModule重載的這兩個虛函數過程分別觸發的。
TObject的建構函式Create和解構函式Destory竟然沒有寫任何代碼,其實,在調試狀態下,通過Debug的CPU視窗,可清楚地反映出Create和Destory的彙編代碼。因為,締造DELPHI的大師門(Hejlsberg...)不想將過多複雜的東西提供給使用者,他們希望使用者在簡單的概念上編寫應用程式,將複雜的工作隱藏在系統的內部由他們承擔。所以,在發布System.pas單元時特別將這兩個函數的代碼去掉,讓使用者認為TObject是萬物之源,使用者派生的類完全從虛無中開始,這本身並沒有錯。
雖然,閱讀DELPHI的這些最本質的代碼需要少量的組合語言知識,但閱讀這樣的代碼,可以讓我們更深刻認識DELPHI世界的起源和發展的基本規律。即使看不太懂,能起碼瞭解一些基本東西,對我們編寫DELPHI程式也是大有協助。
那是虛擬方法表在記憶體中的情況,可以清楚的看到 self(此 self 指對象的self) 指標就指向了虛擬方法表的入口,而 self (此 self 指類的self) 指標則位於虛擬方法表的入口地址-76的位置。
當然這不具有通用性,因為在將來的delphi版本這個位置可能會變.
self之上就是RTTI資訊。所以delphi實現RTTI是與C++不同的,C++靠宏。
Delphi則依靠編譯器,是與delphi目前的版本的編譯器緊密相關的。
不過反正做Pascal編譯器的就Borland一家.....
虛擬方法表:$ system.pas
{ Virtual method table entries }
vmtSelfPtr = -76;
vmtIntfTable = -72;
vmtAutoTable = -68;
vmtInitTable = -64;
vmtTypeInfo = -60;
vmtFieldTable = -56;
vmtMethodTable = -52;
vmtDynamicTable = -48;
vmtClassName = -44;
vmtInstanceSize = -40;
vmtParent = -36;
vmtSafeCallException = -32;
vmtAfterConstruction = -28;
vmtBeforeDestruction = -24;
vmtDispatch = -20;
vmtDefaultHandler = -16;
vmtNewInstance = -12;
vmtFreeInstance = -8;
vmtDestroy = -4;
vmtQueryInterface = 0;
vmtAddRef = 4;
vmtRelease = 8;
vmtCreateObject = 12;
}
參考資料:
1 <天方夜談VCL>
2 <Delphi的原子世界>
DELPHI 對象的本質 VMT