從 Form1.Caption = “Hello World”說起 (轉)

worldblog發表於2007-12-12
從 Form1.Caption = “Hello World”說起 (轉)[@more@]

 


從Form1.Caption = “Hello World” 說起


  與很多人一樣,我的第一個也是從開始的,我們的文章標題就是來自VB中的一條簡單語句,作用是改變程式窗體標題。當然在不同的開發工具中也可以表現為以下形式:


Visual Basic : 


Form1.Caption = “Hello World” 或 me.Caption = “Hello World” 或直接 Caption = “….”


C++Builder:  Form1->Caption = “Hello World”;


  Form1.Caption := ‘Hello World’;


Visual C++ :    SetWindowText(“Hello World”);


以及原始的SDK中的實現形式:


SetWindowText(hMain, “Hello World”);  (假設hMain是主窗體的控制程式碼)


這麼列出來不是為了比較語言的不同,我們的問題是:當我們簡單的將一個字串賦給Caption(這兒也可以是別的變數名)後,又做了那些事來設定窗體標題的。答案很簡單:程式語言可以有很多種,但Windows只有一個,它們都了:SetWindowText(),或直接發一個WM_SETTEXT訊息給指定窗體。

SetWindowText函式原型為:

BOOL SetWindowText(

  HWND hWnd,  // handle of window or control

  LPCTSTR lpString // address of string

  );


兩個引數分別為:窗體控制程式碼,與字串地址,並且根據操作的成功與否返回:TRUE, FALSE;由此看來Visual C++的呼叫形式倒比較容易理解,MFC類庫封裝了一組同名函式,並透過C++類的封裝,簡化了操作,具體的MFC實現大致是如下形式:


void CWnd::SetWindowText(LPCTSTR lpszString)

{


  ASSERT(::IsWindow(m_hWnd)); //先檢查m_hWnd是否為有效的窗體控制程式碼


//呼叫Api函式,m_hWnd是CWnd類的內部成員變數,代表該窗體的控制程式碼

  ::SetWindowText(m_hWnd, lpszString);

}

這是MFC簡化了Windows程式設計的一個表現。


當然SetWindowText不只是用來置對話方塊或主窗體標題,由於我們在螢幕上看到的各種按鈕(Button),編輯框(Edit), 文字標籤(Static或Label)等實際上都是大大小小的窗體或子窗體,因此你也可以由此設定某個按鈕的標題。反之要獲得窗體的標題可以用GetWindowText函式,函式原型與SetWindowText類似。雖然Set, Get, WindowText很容易理解但還是沒有簡單的:


設定標題用: s1 =  “Hello World”, Caption = s1

獲得標題用 s1 =  Caption

看起來更直觀。

好的,那麼我們就來看看C++Builder與Delphi中是如何實現的。

在剛剛使用C++Builder的時候,我除了驚訝C++Builder設計應用程式的快捷方便外,另一個令人困惑的問題就是當我寫了 Form1->Caption = “Hello World”;時C++Builder是如何實現SetWindowText操作的,熟悉C++的朋友都知道: 假設Form1是一個類的例項,那麼當我們寫出 Form1.bOk = true; 或Form1.nSize = 12;這樣的語句時我們是在對一個類的成員變數賦值。如果Form1是一個指標則以上句子就是: Form1->bOk = true; Form1->nSize = 12;而對一個變數成員賦值的單條語句除了簡單的賦值操作外,按照C++的語法並不會做別的事。如果要進行有關操作如顯示一個訊息框,就應該呼叫相應的成員函式。當時我的理解是C++Builder一定是在內部呼叫了相應的Set,Get開頭的成員函式。並且沒有理由的認為編譯器是當我們按下開始編譯後先掃描一下當前的程式碼行,碰到Caption = “….”這樣的語句就把它替換為內部的 Form1.SetCaption(“….”);


當然事情不會這麼簡單,由於C++Builder與Delphi共享同一個VCL類庫,甚至可以使用以Pascal語言編寫的庫,因此C++Builder在某種意義上講已經不是一個純粹的C++編譯環境了,顯然Borland公司對C++Builder做了一些小手腳,使之與Delphi相相容。這麼做好處很多,二者使用類似的環境,使用同一種窗體(*.dfm)格式,最重要的是使用同一個VCL類庫。只要使用過一種開發環境後,就很容易掌握另一個開發工具。缺點是在我們在C++Builder中程式時很容易就會跟蹤到一大堆以begin, end開始的Pascal語句。也就是前面說的二者使用同一個VCL類庫的結果。很遺憾當時我對Pascal語言一竅不通,也沒興趣看,因此對於C++Builder中的實現形式還是不清楚,只是隱隱約約覺得是C++Builder呼叫的某些Pascal程式碼在實現以上操作。 :namespace prefix = o ns = "urn:schemas--com::office" />


後來因為”工作需要”,要維護一個用Delphi寫的考試程式,臨時突擊了一下Pascal語言。其中看到Pascal類的實現時才終於解決了我在C++Builder中的疑問。與C++類似,在 Pascal中也支援相似的類實現。有自己的:private, protected,public,以及類的繼承等等。一個簡單的Pascal類大致為以下形式:

  TMyPoint = class(TObject)

  private

  FX, FY : Integer;

  public

  function PtInRect(const r : TRect) : boolean;{判斷當前的點是否在指定的Rect中}

  function GetX : integer;

  function GetY : Integer;


  //設定FX 為 nX,並進行錯誤檢測,也可以根據需要某些操作

  procedure SetX(const nX : integer);

  procedure SetY(const nY : integer);

 end;


其中的SetX, GetX分別用來設定與獲得當前的X位置。將FX設為類的私有變數並透過SetX函式設定FX的好處在於當呼叫SetX時若給出了一個不合適的值(如:負數,或超出特定範圍)時能夠給出一個Assert斷點,以提醒程式設計師,或在實際執行中一律將不合適的值轉換為0,並丟擲一個異常。


當然我們會想如果既能簡單的: pt.X := 12;  a1 := pt.Y;而且在賦值時又能進行錯誤檢測就更方便了;幸運的是Borland的程式設計師在設計VCL (Visual Component Library)時也是這麼想的,由此在Delphi中引入了一組新的關鍵字: property,read,write;以實現以上需要,改寫的TmyPoint類為如下形式: 


1:  TMyPoint = class(TObject)

2:  private

3:  FX, FY : Integer;

4:  procedure SetX(const nX : integer) ;

5:  procedure SetY(const nY : integer) ;

6:  function GetX : Integer;

7:  function GetY : Integer;

8:  public

9:  {判斷當前的點是否在指定的Rect中}

10:  function PtInRect(const r : TRect) : boolean;

11:  property X : Integer read GetX write SetX ;

12:  property Y : Integer read GetY write SetY ;

13: end;

14:

15: procedure TMyPoint.SetX(const nX: integer);

16: begin

17:  if (nX<0) or (nx>1000) then

18:  FX := 0

19:  else

20:  FX := nX;

21: end;

22:

23: function TMyPoint.GetX: Integer;

24: begin

25:  result := FX;

26: end;

27:


這樣當你執行: pt.X := 200時編譯器知道你要執行一個write操作,就會自動跳到procedure TMyPoint.SetX(const nX: integer);處以進行賦值,反之當為read操作時則呼叫GetX函式。如果GetX只是簡單的返回內部的FX值也可以將11行改為:

property X : Integer read FX write SetX ;


由此可知:當我們在Delphi,或C++Builder中寫下Form1.Caption = “…..”時,真正執行的是某個具體的函式,而不是賦值操作,在這背後並沒有什麼魔法存在,一切是那麼的清楚。如同C++的運算子過載一樣,Delphi也過載了類的”.”運算子,這麼做不僅看起來更直觀,減少了程式碼書寫,從某種意義上講也體現了面向的思想。


好了,該談談VB了,實際上VB並不是一個完全物件導向的語言,缺少許多必備的語言特性,如雖然提供了類的實現,卻不允許類的繼承,此外在型別檢查,錯誤處理上也不夠健壯。最糟糕的是並沒有像VC6.0一樣提供類似的MFC。當你想跟蹤VB中的InputBox,Msgbox,Sqr,trim函式時你是看不到具體實現的。這樣就阻止了我們去了解VB中 Caption = “Hello World”的實現原理,當然擅長DE的朋友可以自己去發掘。好訊息是據說在中BASIC語言已被擴充套件為完全的面嚮物件語言,並支援異常處理。我翻了幾本VB.net與的書,感覺上VB.net中的Basic更像C++語言了,比如在異常處理這一塊,而C#倒很像現在的VB,增加了一大堆”.”操作。(順便說一句,因為在學習.Net上大家還處在盲人摸象的階段,所以書店裡的.Net系列圖書大都很糟糕,請小心挑選。)

小結:

本文簡單的分析了VB,VC,C++Builder,Delphi中Caption = “…”操作的實現原理。所謂“存在的就是合理的“不等於我們看到就都是理所當然的。在進行程式設計時,多問幾個為什麼有助於提高自己的程式設計水平。


好了,不經意間拉拉扯扯寫了這麼多,唯一的問題是這樣的文章適合什麼人看,高手不用看,太簡單了。新手又可能看不大懂我在說什麼:p,如果你喜歡這篇文章或有什麼問題與建議請與我聯絡(to:fpefans@sina.com">fpefans@sina.com)。


 


          by 紀楊 2-5-2002


 


如要轉載,請保持文章的完整性,別的隨便。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991716/,如需轉載,請註明出處,否則將追究法律責任。

相關文章