delphi類,自定義類的大體描述.


剛剛接觸的Delphi的朋友,可能最感興趣的就是它豐富、強大的VCL(可視化構件庫)。僅僅向窗體上扔幾個構件,甚至不用動手寫代碼,就能很容 易地做出一個有實用價值的程序,真是令人激動。但是,VCL只是Delphi的一小部分,Delphi的優秀遠遠不只是表現在VCL上。如果你僅僅停留在 使用VCL這一階段,那么你永遠也不可能成為一個真正的Delphi高手。記住,必須超越VCL,才能夠接觸到Delphi的核心。
   那么,在Delphi的VCL后面,到底隱藏着什么呢?本文將討論兩個比較高級的Delphi主題:OOP和數據庫編程。
  本文假定讀者已經具有Delphi編程的基礎知識,例如,熟悉Pascal語言的一般語法,掌握簡單的VCL使用,會使用Data-Ware構件編寫基本的數據庫程序,等等。本文不會重復VCL的使用這樣的問題。
  
  1、OOP
  
  OOP的英文全稱是Object Oriented
Programming,翻譯過來就是面向對象編程。OOP是一種非常重要的編程思想。也許你會對這種抽象的東西不感興趣,可是幾乎任何一個高手都會告訴你:“語言並不重要,重要的是編程思想。”
  大家知道,Delphi的語言基礎是Object
Pascal。這是Borland在傳統的Pascal語言中增加了面向對象的特性后發展而成,並且特地冠以Object的字樣,以示與傳統的Pascal語言的差別,可見面向對象技術對其影響之大。可以說,Delphi構建在Object
Pascal的基礎之上,而Object Pascal構建在面向對象技術之上。
  事實上,不但Delphi,OOP也是C++、Java等其他許多現代編程語言的基礎(Visual
Basic不完全地支持OOP)。熟練地掌握OOP技術,是深入掌握Delphi的必要條件,是邁入高手境界的必經之路,也是一個成熟的程序員的標志之一。理解了OOP技術之后,很多以前令你困惑的東西會迎刃而解。
  有趣的是,雖然Delphi是完全基於OOP的,但是一個完全不了解OOP的程序員也能夠使用Delphi編寫程序,因為Delphi會自動完成絕大 多數的工作。當你開始學習Delphi的時候,你可能無法想象,當簡單地往窗體上添加一個按鈕時,Delphi會完成多么復雜的工作吧!但是既然有志於深 入Delphi的世界,成為一個真正的程序員,我們就應該對Delphi的細節具有好奇心。
  這些理論可能會讓人覺得枯燥和望而生畏,其實當你掌握了它之后就會覺得沒什么了。當然,你需要有足夠的毅力。
  OOP有三個主要的特征:
  
  1.1 數據封裝
  
  讓我們先看一段代碼:
  
  type
  TDate = class
  Mouth,day,Year:Integer;
  procedure SetValue(m,d,y:Integer);
  function LeapYear:Boolean;
  end;
  
  我們首先會看到class關鍵字,它的中文譯名為“類”。類是一個非常重要的概念。根據權威的定義,類是:一種用戶定義的數據類型,它具有自己的說明 和一些操作。一個類中含有一些內部數據和一些過程或函數形式的對象方法,通常來描述一些非常相似的對象所具有的共同特征和行為。
  這個定義可能比較晦澀。你可以把類想象為一種特殊的Record類型,其中不但可能包含數據,而且可能包含函數和過程(在OOP中稱之為方法)。這些數據和方法被統稱為類的成員。
  上面這個類很顯然是一個日期類型,它包括Mouth,Day,Year這三個數據成員,和SetValue、LeapYear這兩個方法。順便說一句,在Delphi中,習慣以字母T作為每個類的前綴,就象Viusal
C++中習慣以字母C作為每個類的前綴一樣。
  Mouth,Day,Year這三個數據成員指定了該日期的年、月、日。SetValue方法為這三個數據成員賦值,而LeapYear檢查當前日期所在的那一年是否是閏年。下面我們給出這兩個方法的實現部分:
  
  procedure TDate.SetValue(m,d,y):Integer;
  begin
  Mouth := m;
  Day := d;
  Year := y;
  end;
  
  function TDate.LeapYear:Boolean;
  begin
  if (Year mod 4 <> 0) then
  LeapYear := False
  else if (Year mod 100 <> 0)
  LeapYear := True
  else if (Year mod 400 <> 0)
  LeapYear := False
  else
  LeapYear := True;
  end;
  
  實現了這些定義之后,就可以這樣調用它們:
  
  var
  ADay: TDate;
  begin
  //建立一個對象
  ADay := TDate.create;
  //使用之
  ADay.SetValue(1,1,2000);
  if ADay.LeapYear then
  ShowMessage('閏年:' + Inttostr(ADay.year));
  //釋放對象
  ADay.free;
  end;
  
  我們來逐行解釋這些代碼的含義。var后面那一行聲明了一個TDate類的變量。
  聲明了變量之后,我們怎么使用它呢?使用TDate類的Create方法可以建立一個該類的對象,並將其賦予ADay變量。
  現在我們又接觸到了OOP中另一個重要的概念:對象。什么是對象?簡言之,對象就是類的實例,或者說,是類定義的數據類型的變量。當建立一個類的對象 時,系統為它分配一塊內存。例如我們定義一個變量A為Integer類型,那么,Integer是一個數據類型,A就是一個實例。類與對象的關系就類似於 這兩者之間的關系。區別類和對象是非常重要的,甚至一些專業的程序員都往往將他們搞混。
  細心的讀者可能注意到,在TDate類的定義中,並沒有Create這個方法。那么這個Create方法是從哪里來的呢?Create方法是每一個 Class都具有隱含的方法,它的作用是建立這個類的實例。請注意,在這里,類和其他的數據類型是不同的。其他的數據類型都是聲明了變量之后就可以直接使 用,而類類型必須在使用Create方法創建它的實例(對象)之后才能使用。
  事實上,在C++和其他大多數的OOP語言中,聲明一個類的變量就能夠同時建立起這個類的對象。而Delphi(包括它的孿生兄弟C++
Builder)在這方面與眾不同,必須要Create一下才能真正建立對象。同時,在這個對象不再需要時,必須要手工調用free方法釋放這個對象(當 然,free方法也是每個類隱含的)。這和Delphi獨特的“對象引用模型”有關,有興趣的朋友可以查閱有關資料,我就不多說了。
  這種情況造成了一個非常有趣的現象,那就是,編程的初學者往往忘記在使用對象之前create它,從而出錯,但從C++轉向Delphi的高手也常常犯同樣的錯誤……
  順便告訴大家一個訣竅,當編譯器出現“Read of Address:
ffffffff”這樣的錯誤時,多半是因為在使用對象之前忘了Create,可以從這方面入手檢查代碼。另外,也千萬不要忘記在不需要它時使用free釋放掉,否則可能造成內存泄漏。
  在建立和釋放對象的代碼的中間,是使用對象的代碼。訪問對象的數據成員非常簡單,和Record類型沒有什么區別。可以點號表達式來訪問它們:
  
  ADay.Year := 2000;
  ADay.Mouth := 1;
  ADay.Day := 1;
  
  同樣,也可以使用點號表達式來調用對象的方法。如果你閱讀了方法實現部分的代碼,你可以很容易地發 現,ADay.SetValue(1,1,2000)這一句分別為三個數據成員賦了值,而ADay.LeapYear調用則返回當前日期所在年是否為閏 年。至此,整段代碼的意義也就清楚了。
  然而,類不僅僅這么簡單。上面這個例子是一個非常簡單的類,可以直接訪問它的任何成員(數據和方法)。但某些類的成員是不能被隨便訪問的。Delphi中用三個關鍵字區分這些成員的訪問權限:
  
  表1
      Private該類型的成員只能在聲明類中被訪問
      Public該類型的成員可以被程序中的任何地方的代碼訪問
      Protected該類型的成員只能在聲明類以及聲明類的派生類中被訪問


  
  Protected類型的成員以及什么是“派生類”等問題我們留到以后再進行討論,現在我們將注意力集中在前兩者。
  Public類型就是在上面例子中的那種類型,這個很好理解。而Private類型,根據表格中的簡單解釋,只能在該成員被聲明的那個類(也就是該成 員所屬的那個類啦)中被訪問,越出這個界限,它就是不可見的。那么,Private類型的成員將如何被使用呢?簡單地說,就是通過一個Public類的方 法來訪問它。
  讓我們看一個新的例子:
  
  type
  TDate = class
  private
  Mouth,day,Year:Integer;
  Public
  procedure SetValue(m,d,y:Integer);
  function LeapYear:Boolean;
  function GetText:String;
  end;
  
  
  在這個類中,Mouth,Day,Year這三個成員被聲明為Private成員,因此它們在類以外的其它地方是不可訪問的。也就是說,如果你使用
  
  ADay.Year := 2000;
  
  這樣的代碼,那么編譯器將會報錯。但是,我們可以照樣通過SetValue方法為它們賦值:
  
  ADay.SetValue(1,1,2000);
  
  這行代碼是合法的,因為SetValue本身是TDate類的成員,而且它又是一個Public成員。而使用GetText方法則可以得到當前日期值(這也是得到當期日期值的唯一辦法)。
  這樣的設置使得類的一些成員被隱含起來,用戶只能用一些專門的方法來使用它們。那些可以被外部代碼訪問的成員稱之為類的接口。這樣做有什么好處呢?首 先,這讓類的作者可以檢測被賦值的內容。比如,用戶可能給一個對象賦予13月40日這樣的無效日期。而在隱含了一些成員之后,類的作者可以在方法的代碼中 檢測這些值是否有效,從而大大地減少了產生錯誤的機會。其次,使用規范的類,作者可以隨時修改類內部的代碼,而使用該類的代碼卻無需任何修改!這樣使得代 碼的維護成了一件輕松的事件,特別是對於多人協作的大型軟件而言。
  這就叫做數據的封裝(encapsulation)。這是OOP的第一個特征。一個優秀的OOP程序員,應該在設計類的時候,就確定將哪些重要的數據封裝起來,並給出一個高效率的接口。
  需要指出的一點是,表1中Private部分的論述對於“標准的”OOP語言(例如C++)是完全正確的,但對於Delphi有一個例外。在 Delphi中,Private成員除了在聲明類中可以訪問外,在聲明類所在的單元(.pas文件)中的任何地方都能被訪問,不論這些代碼與聲明類的關系 如何。嚴格來說,這是違反OOP的原則的,我不明白Borland為何要這么做(據說是為了方便)。在關於Delphi的優劣性的討論中,這是常被涉及的 一個問題。
  
  1.2 繼承與派生
  
  我們再來看一段代碼:
  
  type
  TNewDate = class(TDate)
  Public
  function GetTextNew:String;
  end;
  
  function GetText:String;
  begin
  return := inttostr(Mouth) + ':' + inttostr(Day) + ':' + inttostr(Year);
  end;
  
  可以看到,在class后面出現一個包含在括號中的類名。這種語法表示新的類繼承了一個舊的類。繼承了原有類的類稱之為派生類,也叫子類,被繼承的類稱之為基類,也叫父類。
  派生類與基類之間是什么關系呢?當派生類繼承自一個基類時,它自動具有基類的所有數據、方法以及其他類型,無須在派生類中再做說明。例如,可以象下面這段代碼這樣使用TNewDate類:
  
  var
  ADay: TNewDate;
  begin
  ADay := TNewDate.create;
  ADay.SetValue(1,1,2000);
  if ADay.LeapYear then
  ShowMessage('閏年:' + Inttostr(ADay.year));
  ADay.free;
  end;
  
  而且,派生類還可以在基類的基礎上加入自己的數據和方法。可以看到在TnewDate類中增加了一個新的方法GetTextNew。下面給出這個方法的實現部分:
  
  function GetTextNew:String;
  begin
  return := GetText;
  end;
  
  然后調用它:
  
  ADay.GetTextNew;
  
  這個新的方法工作得很好。
  為什么GetTextNew方法必須調用基類中的GetText方法,而不能直接使用GetText方法中的那些代碼呢?原因 是,Mouth,Day,Year這三個成員被聲明為Private成員,因此它們即使在派生類中也是不能被訪問的,所以必須調用基類中的GetText 方法,間接地使用它們。如果要直接使用它們的話,可以將這三個成員的屬性從Private改為Protected。在表1中可以看到,Protected 屬性的成員可以在聲明類以及聲明類的派生類中被訪問,然而仍然不能被這兩種情況以外的其他代碼所訪問。現在我們終於可以理解了,這個特殊的屬性實際上提供 了極大的方便:它使得類的成員被封裝,避免了混亂,同時又能夠讓派生類方便地使用它們。
  (如果你是一個細心的人,你可能發現上面的話中間有一個小小的仳漏。當你真的在GetTextNew方法中訪問了基類的Private成員的話,你可 能會驚奇地發現程序也能夠編譯通過而且正常運行!其實,這個問題和OOP本身沒有關系。上面我已經說過,在Delphi中,Private成員在聲明類所 在的單元文件中的任何地方都能被訪問,因此如果TNewDate類和TDate類在同一個.pas文件中時,這種情況就不足為怪了。)
  怎么樣,是不是覺得非常奇妙?通過這種繼承的機制,類不再僅僅是數據和方法的封裝,它提供了開放性。你可以方便地繼承一個功能強大的類,然后添加進自 己需要的特性,同時,你又不需要對基類進行任何的修改。相反,原作者對基類的任何改動,都可以在你的新類中立即反映出來。這非常符合代碼的重用要求。
  這種繼承機制也非常符合現實世界中的情形。可以設想,一般意義上的“動物”是一個類,具有自己的一些特征(成員);而“狗”是“動物”的派生類,它具 有動物的所有特征,同時還具有自己獨有的特征(四條腿,汪汪叫,等等)。而“狗”這個類可以繼續派生下去,例如“黑狗”“白狗”,它們除了保留狗的全部特 征之外,還具有自己的特征(黑顏色,白顏色,等等)。而具體到一只活生生的狗,可以認為它就是“黑狗”或“白狗”(或其他什么狗)的一個實例(對象)。
  OOP這種對現實世界的模擬不僅極大地簡化了代碼的維護,而且使得整個編程思想產生了革命性的變化,較之模塊化編程有了飛躍的進步。
  如果你曾經仔細閱讀過VCL的資料甚至它的源代碼,你就可以發現,整個VCL都是建立在這種強大的封裝-繼承的機制之上的。你可以看到一張詳細的 VCL層次結構圖,就象是一個龐大的家譜,各種VCL構件通過層層繼承而產生。例如,一個簡簡單單的TForm類,就是許多次繼承之后的產物:
  TObject - TPersistent - TConponent - TControl - TWinControl -
TScrollingWinControl - TCustomForm - TForm
  不但Delphi的VCL,Visual C++中的著名的MFC(Microsoft Foundation Class,微軟基本類庫),以及以前Borland
C++中風光一時的OWL(Object Window
Library,對象窗口類庫),都是建立在這種機制之上。所不同的是,對於前兩種語言,你要花上好幾個月的功夫去基本掌握那些繁復無比的類,才能寫出比 較有實用價值的程序,而在Delphi中,大部分的工作Delphi都已經自動幫你完成了。例如,每次你向程序中加入一個窗體時,Delphi就自動為你 從TForm派生一個新類(默認為TForm1),並且為這個新類創造一個實例。你對這個窗體的改動(添加構件和代碼之類),無非是為這個派生類加入一些 新的特性而已;你再也用不着自己去處理最大化、最小化、改變大小這一類的情況,因為這些代碼都在基類中被實現,而被派生類所繼承了。這就是Delphi的 偉大之處。當然,Delphi的VCL也絕不比MFC或OWL遜色(事實上它是由后者演變而來)。
  (可能有人會問起VB的情況。VB不支持繼承,因此並沒有什么復雜的類庫,它自己的控件也少得可憐,主要是使用ActiveX控件。)。
  也許你已經若有所悟,為你的發現而心癢難騷了吧。但是,我們要討論的東西當然不會僅僅這么簡單。
  在1.1部分(“數據封裝”),我們講到了“Create方法是每一個Class都具有隱含的方法”。其實,這種說法是不准確的。事實是,在 Delphi中,所有的類都默認繼承自一個最基礎的類TOject,甚至在你並未指定繼承的類名也是如此。Create方法是TObject類具有的方 法,因此理所當然,所有的類都自動獲得了Create方法,不管你是否實現過它。想想看就知道了:如果沒有Create方法的話,怎樣建立一個對象呢?
  你可能注意到了Create方法是一個特殊的方法。不錯,Create方法的確非常特殊,甚至於它的“頭銜”不再是function或procedure,而是Constructor(構造器)。你可以在VCL的源碼中見到這樣一些例子:
  
  Constructor Create;
  
  構造器不僅是一個Delphi關鍵字,而且是一個OOP方法學的名詞。與之相對應的,還有Destructor(毀壞器)。前者負責完成創建一個對象 的工作,為它分配內存,后者負責釋放這個對象,回收它的內存。要注意的一點是,Constructor的名字一般是Create,但Destructor 的名字卻不是Free,而是Destroy。例如:
  
  Destructor Destroy;
  
  那么,在以前的代碼,為什么又使用Free來釋放對象呢?二者的區別是,Destroy會直接釋放對象,而Free會事實檢查該對象是否存在,如果對 象存在,或者對象不為nil,它才會調用Destroy。因此,程序中應該盡量使用free來釋放對象,這樣更加安全一些。(但要注意,free也不會自 動將對象置為nil,所以在調用free之后,最好是再手動將對象置為nil。)
  象對待一般的函數或過程那樣,也可以向構造器傳遞參數:
  
  type
  TDate = class
  private
  Mouth,day,Year:Integer;
  Public
  function LeapYear:Boolean;
  function GetText:String;
  Constructor Create(m,d,y:Integer);
  end;
  
  procedure TDate.Create(m,d,y):Integer;
  begin
  Mouth := m;
  Day := d;
  Year := y;
  end;
  
  調用它:
  
  ADay: TDate;
  begin
  ADay := TDate.create(1,1,2000);
  if ADay.LeapYear then
  ShowMessage('閏年:' + Inttostr(ADay.year));
  ADay.free;
  end;
  
  這樣,在Create方法里就完成了對數據的初始化,而無須再調用SetValue方法了。
  
  接下來,我們將要涉及到另一個重要的、也是很有趣的問題:方法的虛擬與重載。
  可能你已經有點暈了吧……還是先看一個新的例子:
  
  type
  TMyClass = class
  procedure One;virtual;
  end;
  
  type
  TNewClass = class(TMyClass)
  procedure One;override;
  end;
  
  procedure TMyclass.One;virtual;
  begin
  ShowMessage('調用了TMyclass的方法!');
  end;
  
  procedure TNewClass.One; override;
  begin
  Inherited;
  ShowMessage('調用了TNewClass的方法!');
  end;
  
  可以看到,從TMyClass派生了一個新類TNewClass。這兩個類都聲明了一個名字相同的方法One。所不同的是,在TMyClass中,One方法后面多了一個Virtual關鍵字,表示這個方法是一個虛擬方法(Virtual
Method)。而在TNewClass中,One方法后面多了一個Override關鍵字,表示該方法進行了重載(Override)。重載技術能夠實現許多特殊的功能。
  讓我們來仔細分析它們的實現部分。在TMyclass.One方法的實現部分,調用ShowMessage過程彈出一個對話框,說明該方法已被調用;這里沒有任何特別的地方。在TNewClass.One方法中,出現了一條以前從未出現過的語句:
  
  Inherited;
  
  這個詞的中文意思是“繼承”。我們暫時不要去涉及到太過復雜的OOP概念,只要知道這條語句的功能就是了。它的功能是調用基類中相當的虛擬方法中的代碼。例如,你如果使用以下代碼:
  
  var
  AObject: TNewClass;
  begin
  AObject := TNewClass.create;
  AObject.One;
  AObject.free;
  end;
  
  那么程序將彈出兩次對話框,第一次是調用TMyclass類中的One方法,第二次才是TNewClass.One方法中的代碼。
  重載技術使得我們不但可以在派生類中添加基類沒有的數據和方法,而且可以非常方便地繼承基類中原有方法的代碼,只需要簡單地加入Inherited就 可以了。如果你不加入Inherited語句,那么基類的相應方法將被新的方法覆蓋掉。但是必須注意,重載只有在基類的方法被標志為Virtual時才能 進行,而且重載的方法必須具有和虛擬方法完全相同的參數類型。
  虛擬方法還有一種特例,即抽象方法:
  
  procedure One;override;abstract;
  
  在One方法后面,不但有override關鍵字,還多了一個abstract關鍵字(意為抽象)。這種方法稱為抽象方法(在C++中稱為純虛擬函 數)。含有抽象方法的類稱為抽象類。抽象方法的獨特之處在於,它只有聲明,而根本沒有實現部分,如果你企圖調用一個對象的抽象方法,你將得到一個異常。只 有當這個類的派生類重載並實現了該方法之后,它才能夠被調用。(在C++中,甚至根本就不能建立一個抽象類的實例。)
  既然如此,那么這種抽象方法又有什么用呢?這個問題我們將在接下來的“多態”部分進行討論。
  
  1.3 多態
  
  多態相對來說比較復雜一點。不過不要擔心,它的內容比較少,而且如果以前的知識掌握得比較穩固的話,多態的概念是水到渠成的。
  先來討論一下類型的兼容性問題。下面是一個例子:
  
  type
  TAnimal = Class
  Procedure Voice;virtual;
  ...
  end;
  
  TDog = Class(TAnimal)
  Procedure Voice;Override;
  ...
  end;
  
  implementation
  
  Procedure TAnimal.Voice;virtual;
  Begin
  PlaySound('Anim.wav',0,snd_Async);
  End;
  
  Procedure TDog.Voice;virtual;
  Begin
  PlaySound('Dog.wav',0,snd_Async);
  End;
  
  TDog類繼承了TAnimal類,並重載了其中的Voice方法。PlaySound是一個WIN
API函數,可以播放指定的wav文件。(這個函數的定義在MMSystem.pas文件中可以找到。)
  先看這段代碼:
  
  var
  MyAnimal1, MyAnimal2: TAnimal;
  Begin
  MyAnimal1 := TAnimal.Create;
  MyAnimal2 := TDog.Create;
  ...
  
  在實現部分的第一行中,建立了一個TAnimal類型的對象,並將其賦予TAnimal類型的變量MyAnimal1。這是很正常的事。但在第二行 中,建立了一個TDog類型的對象,並將其賦予了TAnimal類型的變量MyAnimal2。這看上去令人吃驚,但這些代碼是完全合法的。
  眾所周知,Pascal以及Object
Pascal是一種類型定義嚴格的語言,你不能將某個類型的值賦予不同類型的變量,例如將一個整型值賦予布爾型變量,將會導致出錯。但是,這個規則在涉及 到OOP領域時,出現了一個重要的例外,那就是:可以將一個子類的值賦予一個父類類型的變量。但倒過來卻是不行的,一個父類的值決不能賦予一個子類類型的 變量。
  如果將這個原則放到現實世界中,那就很容易理解了:“狗”繼承自“動物”,因為狗也是一種動物。所以可以將一個“狗”類型的值賦予“動物”類型的變量,因為“狗”具有“動物”的一切特征。但反過來,“動物”不具有“狗”的所有特征,因此反向賦值是不行的。
  那么,這種兼容規則在編程中究竟有什么用處呢?
  請注意下面這段代碼:
  
  var
  MyAnimal1, MyAnimal2: TAnimal;
  Begin
  MyAnimal1 := TAnimal.Create;
  MyAnimal2 := TDog.Create;
  MyAnimal1.Sound;
  MyAnimal2.Sound;
  ...
  
  MyAnimal1和MyAnimal2都是TAnimal的變量,而且都調用了Sound方法。但是,執行的結果是完全不同的:前者執行的是 TAnimal.Voice的代碼,而后者執行的是TDog.Voice的代碼!其原因很簡單,因為MyAnimal1被賦予了TAnimal類型的對 象,而MyAnimal2被賦予了TDog類型的對象。也就是說,一個TAnimal類型的變量,當它調用Sound方法時,所執行的代碼是不確定的:可 能執行TAnimal.Voice的代碼,也可能執行的是TDog.Voice的代碼,取決於它當時引用的是一個什么樣的對象。
  再看:
  
  MyAnimal1 := TAnimal.Create;
  MyAnimal1.Sound;
  MyAnimal1.free;
  MyAnimal1 := TDog.Create;
  MyAnimal1.Sound;
  ...
  
  同一個變量MyAnimal1,在第一次調用Sound方法時,執行的是TAnimal.Voice的代碼,在第二次時執行的是TDog.Voice 的代碼。MyAnimal1.Sound這行代碼不需要變化,程序可以根據不同的情況賦予該變量不同的對象,從而使它執行不同的代碼。這就是多態的定義。
  這個非常重要的特點大大地增加了代碼的可復用性。如前所述,只需要簡單地寫下一行代碼,就可以讓程序執行不同的功能,因為這個虛擬方法同 TAnimal的任何派生類都是兼容的,甚至連那些還沒有編寫出來的類也是一樣。而程序員並不需要了解這些派生類的細節。利用多態性寫出來代碼,還具有簡 潔和維護性好的特點。
  現在我們可以回到本文的1.2節結尾處的問題了。抽象方法本身不能夠做任何事情,必須在子類中被重載並實現,才能夠完成有意義的工作。但抽象方法的存 在,相當於為父類留下了一個接口,當程序將一個子類的對象賦予父類的變量時,父類的變量就可以調用這個方法,當然此時它運行的是相應的子類中重載該方法的 代碼。如果沒有這個抽象方法,父類的變量就不能調用它,因為它不能調用一個只在子類中存在、而在父類中不存在的方法!
  
  關於OOP的介紹就到此這止。在以上這些篇幅里,介紹的只是OOP最基本的一些概念,讓讀者對OOP有一定的系統認識,也為下文的討論打好基礎 。更多、更深入的東西等待着你自己去發掘。
  本文已經多次強調OOP的重要性,這里還要強調一次:對OOP的掌握程度,在某種意義上決定着你對Delphi世界的理解能力。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM