程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> DELPHI的原子世界(1)

DELPHI的原子世界(1)

編輯:Delphi
  在使用Delphi開發軟件的過程中,我們就像草原上一群快樂牛羊,無憂無慮地享受著Object Pascal語言為我們帶來的陽光和各種VCL控件提供的豐富的水草。抬頭望望無邊無際蔚藍的天空,低頭品嘗大地上茂密的青草,誰會去想宇宙有多大,比分子和原子更小的東西是什麼?那是哲學家的事。而哲學家此時正坐在高高的山頂上,仰望宇宙星雲變換,凝視地上小蟲的爬行,蓦然回頭,對我們這群吃草的牛羊點頭微笑。隨手扯起一根小草,輕輕地含在嘴裡,閉上眼睛細細品嘗,不知道這根青草在哲學家的嘴裡是什麼味道?只是,他的臉上一直帶著滿意的微笑。
      認識和了解DELPHI微觀的原子世界,可以使我們徹底理解Delphi的宏觀應用程序結構,從而在更廣闊的思想空間中開發我們的軟件。這就好像,牛頓發現了宏觀物體的運動,卻因為搞不清物體為什麼會這樣運動而苦惱,相反,愛因斯坦卻在基本粒子規律和宏觀物體運動之間體驗著相對論的快樂生活!
  
  第一節  TObject原子
      TObject是什麼?
      是Object Pascal語言體系結構的基本核心,也是各種VCL控件的起源。我們可以認為,TObject是構成Delphi應用程序的原子之一,當然,他們又是由基本Pascal語法元素等更細微的粒子構成。
      說TObject是DELPHI程序的原子,是因為TObject是Delphi編譯器內部支持的。所有的對象類都是從TObject派生的,即使你並未指定TObject為祖先類。TObject被定義在System單元,它是系統的一部分。在System.pas單元的開頭,有這樣的注釋文本:
      { Predefined constants, types, procedures, }
      { and functions (such as True, Integer, or }
      { Writeln) do not have actual declarations.}
      { Instead they are built into the compiler }
      { and are treated as if they were declared }
      { at the beginning of the System unit.    }
      它的意思說,這一單元包含預定義的常量、類型、過程和函數(諸如:Ture、Integer或Writeln),它們並沒有實際的聲明,而是編譯器內置的,並在編譯的開始就被認為是已經聲明的定義。你可以將Classes.pas或Windows.pas等其他源程序文件加入你的項目文件中進行編譯和調試其源代碼,但你絕對無法將System.pas源程序文件加入到你的項目文件中進行編譯!Delphi將報告重復定義System的編譯錯誤!
      因此,TObject是編譯器內部提供的定義,對於我們使用Delphi開發程序的人來說,TObject是原子性的東西。
      TObject在System單元中的定義是這樣的:
    TObject = class
      constructor Create;
      procedure Free;
      class function InitInstance(Instance: Pointer): TObject;
      procedure CleanupInstance;
      function ClassType: TClass;
      class function ClassName: ShortString;
      class function ClassNameIs(const Name: string): Boolean;
      class function ClassParent: TClass;
      class function ClassInfo: Pointer;
      class function InstanceSize: Longint;
      class function InheritsFrom(AClass: TClass): Boolean;
      class function MethodAddress(const Name: ShortString): Pointer;
      class function MethodName(Address: Pointer): ShortString;
      function FIEldAddress(const Name: ShortString): Pointer;
      function GetInterface(const IID: TGUID; out Obj): Boolean;
      class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
      class function GetInterfaceTable: PInterfaceTable;
      function SafeCallException(ExceptObject: TObject;
        ExceptAddr: Pointer): HResult; virtual;
      procedure AfterConstruction; virtual;
      procedure BeforeDestruction; virtual;
      procedure Dispatch(var Message); virtual;
      procedure DefaultHandler(var Message); virtual;
      class function NewInstance: TObject; virtual;
      procedure FreeInstance; virtual;
      destructor Destroy; virtual;
    end;
  
      下面,我們將逐步敲開TObject原子的大門,看看裡面到底是什麼結構。
  
      我們知道,TObject是所有對象的基本類,那麼,一個對象到底是什麼?
    Delphi中的任何對象都是一個指針,這個指針指明該對象在內存中所占據的一塊空間!雖然,對象是一個指針,可是我們引用對象的成員時卻不用寫成這樣的代碼MyObject^.GetName,而只能寫成MyObject.GetName,這是Object Pascal語言擴充的語法,是由編譯器支持的。使用C++ Builder的朋友就很清楚對象與指針的關系,因為在C++ Builder的對象都要定義為指針。對象指針指向的地方就是對象存儲數據的對象空間,我們來分析一下對象指針指向的內存空間的數據結構。
      對象空間的頭4個字節是指向該對象類的虛方法地址表(VMT ?C Vritual Method Table)。接下來的空間就是存儲對象本身成員數據的空間,並按從該對象最原始祖先類的數據成員到該對象類的數據成員的總順序,和每一級類中數據成員的定義順序存儲。
      類的虛方法地址表(VMT)保存從該類的原始祖先類派生到該類的所有類的虛方法的過程地址。類的虛方法,就是用保留字vritual聲明的方法,虛方法是實現對象多態性的基本機制。雖然,用保留字dynamic聲明的動態方法也可實現對象的多態性,但這樣的方法不保存在虛方法地址表(VMT)中,它只是Object Pascal提供的另一種可節約類存儲空間的多態實現機制,但卻是以犧牲調用速度為代價的。
      即使,我們自己並未定義任何類的虛方法,但該類的對象仍然存在指向虛方法地址表的指針,只是地址項的長度為零。可是,在TObject中定義的那些虛方法,如Destroy、FreeInstance等等,又存儲在什麼地方呢?原來,他們的方法地址存儲在相對VMT指針負方向偏移的空間中。其實,在VMT表的負方向偏移76個字節的數據空間是對象類的系統數據結構,這些數據結構是與編譯器相關的,並且在將來的Delphi版本中有可能被改變。
      因此,你可以認為,VMT是一個從負偏移地址空間開始的數據結構,負偏移數據區是VMT的系統數據區,VMT的正偏移數據是用戶數據區(自定義的虛方法地址表)。TObject中定義的有關類信息或對象運行時刻信息的函數和過程,一般都與VMT的系統數據有關。
      一個VMT數據就代表一個類,其實VMT就是類!在Object Pascal中我們用TObject、TComponent等等標識符表示類,它們在Delphi的內部實現為各自的VMT數據。而用class of保留字定義的類的類型,實際就是指向相關VMT數據的指針。
      對我們的應用程序來說,VMT數據是靜態的數據,當編譯器編譯完成我們的應用程序之後,這些數據信息已經確定並已初始化。我們編寫的程序語句可訪問VMT相關的信息,獲得諸如對象的尺寸、類名或運行時刻的屬性資料等等信息,或者調用虛方法或讀取方法的名稱與地址等等操作。
      當一個對象產生時,系統會為該對象分配一塊內存空間,並將該對象與相關的類聯系起來,於是,在為對象分配的數據空間中的頭4個字節,就成為指向類VMT數據的指針。
      我們再來看看對象是怎樣誕生和滅亡的。看著我三歲的兒子在草地上活蹦亂跳,正是由於親眼目睹過生命的誕生過程,我才能真真體會到生命的意義和偉大。也只有那些經歷過死別的人,才會更加理解和珍惜生命。那麼,就讓我們理解一下對象的產生和消亡的過程吧!
      我們都知道,用下面的語句可以構造一個最簡單對象:
        AnObject := TObject.Create;
      編譯器將其編譯實現為:
      用TObject對應的VMT為依據,調用TObject的Create構造函數。而在Create構造函數調用了系統的ClassCreate過程,系統的ClassCreate過程又通過存儲在類VMT調用NewInstance虛方法。調用NewInstance方法的目的是要建立對象的實例空間,因為我們沒有重載該方法,所以,它就是TObject類的NewInstance。TObjec類的NewInstance方法將根據編譯器在VMT表中初始化的對象實例尺寸(InstanceSize),調用GetMem過程為該對象分配內存,然後調用InitInstance方法將分配的空間初始化。InitInstance方法首先將對象空間的頭4個字節初始化為指向對象類對應VMT的指針,然後將其余的空間清零。建立對象實例之後,還調用了一個虛方法AfterConstruction。最後,將對象實例數據的地址指針保存到AnObject變量中,這樣,AnObject對象就誕生了。
      同樣,用下面的語句可以消滅一個對象:
        AnObject.Destroy;
      TObject的析構函數Destroy被聲明為虛方法,它也是系統固有的虛方法之一。Destory方法首先調用了BeforeDestruction虛方法,然後調用系統的ClassDestroy過程。ClassDestory過程又通過類VMT調用FreeInstance虛方法,由FreeInstance方法調用FreeMem過程釋放對象的內存空間。就這樣,一個對象就在系統中消失。
      對象的析構過程比對象的構造過程簡單,就好像生命的誕生是一個漫長的孕育過程,而死亡卻相對的短暫,這似乎是一種必然的規律。
  在對象的構造和析構過程中,調用了NewInstance和FreeInstance兩個虛函數,來創建和釋放對象實例的內存空間。之所以將這兩個函數聲明為虛函數,是為了能讓用戶在編寫需要用戶自己管理內存的特殊對象類時(如在一些特殊的工業控制程序中),有擴展的空間。
      而將AfterConstruction和BeforeDestruction聲明為虛函數,也是為了將來派生的類在產生對象之後,有機會讓新誕生的對象呼吸第一口新鮮空氣,而在對象消亡之前可以允許對象完成善後事宜,這都是合情合理的事。其實,TForm對象和TDataModule對象的OnCreate事件和OnDestroy事件,就是在TForm和TDataModule重載的這兩個虛函數過程分別觸發的。
  此外,TObjec還提供了一個Free方法,它不是虛方法,它是為了那些搞不清對象是否為空(nil)的情況下能安全釋放對象而專門提供的。其實,搞不清對象是否為空,本身就有程序邏輯不清晰的問題。不過,任何人都不是完美的,都可能犯錯,使用Free能避免偶然的錯誤也是件好事。然而,編寫正確的程序不能一味依靠這樣的解決方法,還是應該以保證程序的邏輯正確性為編程的第一目標!
      有興趣的朋友可以讀一讀System單元的原代碼,其中,大量的代碼是用匯編語言書寫的。細心的朋友可以發現,TObject的構造函數Create和析構函數Destory竟然沒有寫任何代碼,其實,在調試狀態下通過Debug的CPU窗口,可清楚地反映出Create和Destory的匯編代碼。因為,締造DELPHI的大師門不想將過多復雜的東西提供給用戶,他們希望用戶在簡單的概念上編寫應用程序,將復雜的工作隱藏在系統的內部由他們承擔。所以,在發布System.pas單元時特別將這兩個函數的代碼去掉,讓用戶認為TObject是萬物之源,用戶派生的類完全從虛無中開始,這本身並沒有錯。雖然,閱讀Delphi的這些最本質的代碼需要少量的匯編語言知識,但閱讀這樣的代碼,可以讓我們更深刻認識DELPHI世界的起源和發展的基本規律。即使看不太懂,能起碼了解一些基本東西,對我們編寫Delphi程序也是大有幫助。
  
  第二節  TClass原子
      在System.pas單元中,TClass是這樣定義的:
        TClass = class of TObject;
      它的意思是說,TClass是TObject的類。因為TObject本身就是一個類,所以TClass就是所謂的類的類。
      從概念上說,TClass是類的類型,即,類之類。但是,我們知道Delphi的一個類,代表著一項VMT數據。因此,類之類可以認為是為VMT數據項定義的類型,其實,它就是一個指向VMT數據的指針類型!
  在以前傳統的C++語言中,是不能定義類的類型的。對象一旦編譯就固定下來,類的結構信息已經轉化為絕對的機器代碼,在內存中將不存在完整的類信息。一些較高級的面向對象語言才可支持對類信息的動態訪問和調用,但往往需要一套復雜的內部解釋機制和較多的系統資源。而Delphi的Object Pascal語言吸收了一些高級面向對象語言的優秀特征,又保留可將程序直接編譯成機器代碼的傳統優點,比較完美地解決了高級功能與程序效率的問題。
      正是由於Delphi在應用程序中保留了完整的類信息,才能提供諸如as和is等在運行時刻轉換和判別類的高級面向對象功能,而類的VMT數據在其中起了關鍵性的核心作用。有興趣的朋友可以讀一讀System單元的AsClass和IsClass兩個匯編過程,他們是as和is操作符的實現代碼,以加深對類和VMT數據的理解。
  
      ......
      後面的內容還有對虛構造函數的理解,Interface的實現機制和異常處理的實現機制,等等DLPHI的基本原理。希望五一之後能寫完,再貢獻給大家。 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved