幾點知識:
無論父類裡面有沒有虛函數,都可以定義指向子類執行個體的父類指標.
如果父類裡沒有虛函數,則使用父類指標,只能訪問父類的成員,而不能訪問子類裡的成員.
如果父類裡的虛函數不是純虛函數,且子類裡沒有重寫該虛函數,則用父類指標訪問該虛函數的時候,跟訪問父類裡的普通函數一樣.
如果父類裡的虛函數不是純虛函數,且子類裡重寫了該虛函數,則用父類指標訪問該虛函數的時候訪問的是子類裡重寫後的函數.
如果父類裡的虛函數是純虛函數,則父類是個抽象類別,子類要想能夠被執行個體化,則必須重寫該純虛函數.用父類指標訪問該純虛函數的時候,訪問到的是子類裡重寫了的函數.
再有一個要注意的是解構函式要聲明為虛函數,這樣在delete父類指標的時候,才會調用執行個體化的子類的虛函數,否則只會調用父類的解構函式,造成子類的剩餘部分沒被釋放,從而造成記憶體的泄漏.
總結:
當定義一個指向子類執行個體的父類指標的時候,記憶體中執行個體化了子類,由於子類繼承了父類,因此記憶體中的子類裡包含父類的所有成員.但由於聲明的是父類指標,因此該指標不能夠訪問子類的成員.而只能訪問父類的成員.然而在父類裡可以聲明純虛函數和定義虛函數.使用父類指標訪問虛函數或純虛函數的時候,訪問到的是子類裡重寫的函數. 當然,對於虛函數,如果子類裡沒有對其重寫的話,仍然訪問到父類裡定義的虛函數.可見虛函數和純虛函數的卻別僅僅在於:純虛函數沒有定義,只有聲明,而且它使擁有它的類成為抽象類別.
以下內容來自:http://blog.chinaunix.net/uid-20665441-id-305464.html1,如果以一個基礎類指標指向一個衍生類對象(衍生類別對象),那麼經由該指標只能訪問基礎類定義的函數(靜態聯翩)2,如果以一個衍生類指標指向一個基礎類對象,必須先做強制轉型動作(explicit cast),這種做法很危險,也不符合生活習慣,在程式設計上也會給程式員帶來困擾。(一般不會這麼去定義)3,如果基礎類和衍生類定義了相同名稱的成員函數,那麼通過對象指標調用成員函數時,到底調用那個函數要根據指標的原型來確定,而不是根據指標實際指向的物件類型確定。 虛擬函數就是為了對“如果你以一個基礎類指標指向一個衍生類對象,那麼通過該指標,你只能訪問基礎類定義的成員函數”這條規則反其道而行之的設計。 如果你預期衍生類由可能重新定義一個成員函數,那麼你就把它定義成虛擬函數( virtual )。 Polymorphism就是讓處理基礎類別對象的程式碼能夠通透的繼續適當地處理衍生類對象。 純虛擬函數:virtual void myfunc ( ) =0;純虛擬函數不許定義其具體動作,它的存在只是為了在衍生類鐘被重新定義。只要是擁有純虛擬函數的類,就是抽象類別,它們是不能夠被執行個體化的(只能被繼承)。如果一個繼承類沒有改寫父類中的純虛函數,那麼他也是抽象類別,也不能被執行個體化。 抽象類別不能被執行個體化,不過我們可以擁有指向抽象類別的指標,以便於操縱各個衍生類。 虛擬函數衍生下去仍然是虛擬函數,而且還可以省略掉關鍵字“virtual”。
看個例子:
#include <iostream> using namespace std; class A { public: virtual void foo() { cout << "A's foo()" << endl; bar(); } virtual void bar() { cout << "A's bar()" << endl; } }; class B: public A { public: void foo() { cout << "B's foo()" << endl; A::foo(); } void bar() { cout << "B's bar()" << endl; } }; int main() { B bobj; A *aptr = &bobj; aptr->foo(); A aobj = *aptr; //轉化為A類對象 aobj.foo(); } aptr->foo()輸出結果是: B's foo()//這個明白,多態性 A's foo()//這個也明白,執行A::foo(); B's bar()//雖然調用的是這個函數:A::foo(); 但隱式傳入的還是bobj 的地址,所以再次調用bar();調用時還是會調用B的函數, 與虛函數指標有關aobj.foo()輸出結果是: A's foo() //這個不是指標,aobj完全是一個A的對象,與多態沒有關係 A's bar()