1. 程式人生 > >C++(九)— 虛函數

C++(九)— 虛函數

str arch 抽象 實例 都是 方法 tree sid 動態聯編

1、虛函數

  原因:通過指針調用成員函數時,只能訪問到基類的同名成員函數。在同名覆蓋現象中,通過某個類的對象(指針及引用)調用同名函數,編譯器會將該調用靜態聯編到該類的同名函數,也就是說,通過基類對象指針是無法訪問派生類的同名函數的,即使這個指針是用派生類對象來初始化的。

  虛函數是C++中用於實現多態(polymorphism)的機制。核心理念就是通過基類訪問派生類定義的函數。

  指向基類的指針在操作它的多態類對象時,會根據不同的類對象,調用其相應的函數,這個函數就是虛函數。

  可以說,基類聲明的虛函數,在派生類中也是虛函數,即使不再使用virtual關鍵字。

  動態聯編:一個類函數的調用並不是在編譯時刻被確定的,而是在運行時刻被確定的。由於編寫代碼的時候並不能確定被調用的是基類的函數還是哪個派生類的函數,所以被成為“虛”函數。

   虛函數只能借助於指針或者引用來達到多態的效果。

  A *p=new B() 含義:A是指父類,A* p為定義一個父類的指針p,指向子類B。p指針本身是一個父類類型的,也指向A.一個樹Tree和一個蘋果樹AppleTree.指針p既然指向了蘋果樹,肯定也指向樹。

  首先:強調一個概念
  定義一個函數為虛函數,不代表函數為不被實現的函數。
  定義他為虛函數是為了允許用基類的指針來調用子類的這個函數。
  定義一個函數為純虛函數,才代表函數沒有被實現。
  定義純虛函數是為了實現一個接口,起到一個規範的作用,規範繼承這個類的程序員必須實現這個

2、“overload”和“override”

  override是指派生類重寫基類的虛函數,就象我們前面B類中重寫了A類中的foo()函數。重寫的函數必須有一致的參數表和返回值(C++標準允許
返回值不同的情況,這個我會在“語法”部分簡單介紹,但是很少編譯器支持這個feature)。這個單詞好象一直沒有什麽合適的中文詞匯來對應,有人譯為
“覆蓋”,還貼切一些。
  overload約定成俗的被翻譯為“重載”。是指編寫一個與已有函數同名但是參數表不同的函數。例如一個函數即可以接受整型數作為參數,也可以接受浮點數作為參數。

3、純虛函數

如下聲明表示一個函數為純虛函數:

class A
{
public:
virtual void foo()=0; // =0標誌一個虛函數為純虛函數
};

  一個函數聲明為純虛後,純虛函數的意思是:我是一個抽象類!不要把我實例化!純虛函數用來規範派生類的行為,實際上就是所謂的“接口”。它告訴使用者,我的派生類都會有這個函數。

  純虛函數的引入,是出於兩個目的:
  1、為了安全,因為避免任何需要明確但是因為不小心而導致的未知的結果,提醒子類去做應做的實現。
  2、為了效率,不是程序執行的效率,而是為了編碼的效率。

4、虛析構函數

  只有虛析構函數,沒有虛構造函數。

  創建派生類對象時,調用基類構造->派生類構造->派生類析構->基類析構。

  如果用new運算符動態創建派生類對象,並以此對象地址初始化基類指針,構造沒問題,但用delete運算符刪除派生類對象時,由於指針是指向基類的,通過靜態聯編,調用基類析構函數,不調用派生類析構函數,使得派生類無法執行某些清理工作,例如:派生類中申請的內存沒機會還給系統。

  虛析構函數:基類設置虛析構函數,派生類都是。此時使用基類對象指針銷毀派生類對象時,會通過動態聯編調用派生類析構函數,完成派生類的清理工作。

5、使用

  在你設計一個基類的時候,如果發現一個函數需要在派生類裏有不同的表現,那麽它就應該是虛的。從設計的角度講,出現在基類中的虛函數是接口,出現在派生類中的虛函數是接口的具體實現。通過這樣的方法,就可以將對象的行為抽象化。

C++(九)— 虛函數