一、虛方法
C++不將允許CDog對象繼承了基類的屬性(資料)和功能(方法),然而C++還擴充了其多態性。也就是,允許將衍生類別對象賦值給指向基類的指標。
CAnimal *panimal=new CDlog;
然後可以通過這個指標來調用CAnimal類的任何方法。虛函數就可以做到這點。
#include <iostream><br />#include <string><br />using namespace std;<br />class CAnimal<br />{<br /> public:<br /> CAnimal(){cout<< "animal constructor" <<endl;}<br /> virtual ~CAnimal(){cout<< "animal destructor" <<endl;}<br /> virtual void speek() const{cout<< "animal speek" <<endl;}<br /> void move () const {cout<< "animal move one tesp" <<endl;}<br />};<br />class CDog:public CAnimal<br />{<br /> public:<br /> CDog(){ cout<< "dog constructor" <<endl;}<br /> virtual ~CDog(){cout<< "dog destructor" <<endl;}<br /> virtual void speek() const {cout<< "dog speek" <<endl;}<br /> void move () const {cout<< "dog move one tesp" <<endl;}<br />};<br />int main()<br />{<br /> CAnimal *pDog = new CDog;<br /> pDog->speek();<br /> pDog->move();<br /> delete pDog;<br /> return 0;<br />}
[root@local~ work]# ./a.out
animal constructor
dog constructor
dog speek
animal move one tesp
dog destructor
animal destructor
二、虛函數工作原理
建立派生對象時,首先調用基類的建構函式,然後調用衍生類別的建構函式。說明了CDog對象被建立後的情景,注意CAnimal部分和CDog部分在記憶體中是相鄰的。
在類建立虛方法後,這個類必須被跟蹤虛方法。很多編譯器建立了虛函數表。每個類都有一個虛函數表,每個類對象都有一個執行虛函數表的指標(vptr或v-pointer)。每個對象的vptr都指向v-talbe;而對於每個虛方法,v-table都包含一個指向它的指標。建立CDog的CAnimal部分時,vptr被初始化指向v-table的正確部分。如
當CDog的建構函式被調用時,添加對象的CDog部分,並調整vptr指標使其指向CDog類中覆蓋的虛方法。如
使用CAnimal指標時,vptr將根據CAnimal指標指向的對象的實際類型指向正確的函數,這樣調用函數speak時,將調用正確的函數。
三、通過基類指標訪問衍生類別的方法
如果只有衍生類別有該方法,基類沒有對應的虛方法,則不能通過CAnimal指標來訪問該方法。不過可以將CAnimal指標強制轉為CDog指標,但是CAnimal不是一個CDog,這樣做將是不安全的。
四、切除
僅當通過指標或引用進行調用時,才能發揮虛函數的魔力,按值傳遞對象將不能發揮虛方法的魔力。
五、建立虛解構函式
當指向衍生類別對象的指標被刪除時將發生什麼情況呢?如果解構函式時虛函數,將執行正確的操作:調用衍生類別的解構函式,衍生類別的解構函式會自動調用基類的解構函式,因此整個對象將被正確地銷毀。
經驗規則,如果類中任何一個函數都是虛函數,解構函式也應該是虛函數。
六、虛複製建構函式
建構函式不能是虛函數,因此從技術上,不存在虛複製建構函式。然而有時候程式非常需要通過傳遞一個指向基類對象的指標,建立一個衍生類別對象的副本。一個常見的解決方案是,在基類建立一個clone方法,並將其設定為虛方法。clone方法建立當前類對象一個副本,並返回該副本。
由於每個衍生類別都覆蓋了clone方法,因此他將建立衍生類別對象的一個副本。
#include <iostream><br />#include <string><br />using namespace std;<br />class CAnimal<br />{<br /> public:<br /> CAnimal(){cout<< "animal constructor" <<endl;}<br /> CAnimal(const CAnimal & ranimal){cout<< "animal copy constructor" <<endl;}<br /> virtual ~CAnimal(){cout<< "animal destructor" <<endl;}<br /> virtual void speek() const{cout<< "animal speek" <<endl;}<br /> virtual CAnimal* clone () {return new CAnimal(*this);}<br />};<br />class CDog:public CAnimal<br />{<br /> public:<br /> CDog(){ cout<< "dog constructor" <<endl;}<br /> CDog(const CDog & rdog){cout<< "dog copy constructor" <<endl;}<br /> virtual ~CDog(){cout<< "dog destructor" <<endl;}<br /> virtual void speek() const {cout<< "dog speek" <<endl;}<br /> void move() const {cout<< "dog move one step" <<endl;}<br /> virtual CAnimal* clone () {return new CDog(*this);}<br />};<br />int main()<br />{<br /> CAnimal *pDog = new CDog;<br /> pDog->speek();<br /> CAnimal *p = pDog->clone();<br /> p->speek();<br /> delete pDog;<br /> delete p;<br /> return 0;<br />}
[root@local~ work]# ./a.out
animal constructor
dog constructor
dog speek
animal constructor
dog copy constructor
dog speek
dog destructor
animal destructor
dog destructor
animal destructor
七、使用虛方法的代價
由於包含虛方法的類必須維護一個v-table,因此使用虛方法會帶來一些開銷。