基類與派生類,父類指針指向子類對象
先看一段代碼:
1 #include<iostream> 2 3 using namespace std; 4 5 class Base{ 6 public: 7 Base() { cout<<"Base Creted"<<endl; } 8 ~Base() { cout<<"Base Destroyed"<<endl; } 9 }; 10 11 class Derived: public Base { 12 public: 13 Derived() { cout<<"Derived Created"<<endl; } 14 ~Derived() { cout<<"Derived Destroyed"<<endl; } 15 }; 16 17 int main() 18 { 19 Base *pB = new Derived(); 20 delete pB; 21 pB = NULL; 22 return 0; 23 }
運行結果如下,情理之中,意料之內:
C++創建對象的時候先創建基類部分,然後創建派生部分。析構的時候要反過來了,先釋放子類部分,然後在釋放父類部分。但是這裏只釋放了父類部分,沒有釋放派生類的部分。為什麽呢?
原因很明確:因為之類pB是基類指針,雖然指向的是派生類,只能調用自己的函數,我們是無法通過基類指針調用到子類的成員函數的(除非采用virtual,也就是遲綁定技術)。
虛函數
為了不造成內存泄漏,我們將上面代碼改成:
1 class Base{ 2 public: 3 Base() { cout<<"Base Creted"<<endl; } 4 virtual ~Base() { cout<<"Base Destroyed"<<endl; } //virtual關鍵字很關鍵哦 5 };
運行結果正確了:
和之前版本相比,基類的析構函數多了一個 virtual
虛函數通過動態綁定技術實現了C++的運行時的多態性。讓我們可以通過基類的指針或者引用調用派生類的方法。C++中還有一個多態性是編譯時的多態,通過模版實現。
《Effective C++》條款 07 p40:為多態基類聲明virtual析構函數。
《Effective C++》條款 07 p44:
- 帶多態性質的base classes應該聲明一個virtual析構函數。如果class帶有任何virtual函數,它就應該擁有一個virtual析構函數
- Classes的設計目的如果不是作為base classes 使用,或不是為了具備多態性,就不該聲明virtual析構函數。
動態綁定的是怎麽實現的?
1、為每個含義虛函數的類創建一個虛函數表VTable,存到常量區,依次存放虛函數的地址。對於每個派生類來說,如果沒有重寫基類的虛函數,那麽派生類的虛函數表中的函數地址還是基類的那個虛函數地址。
2、為每個含有虛函數的對象創建一個指向VTable的指針VPtr,所以說同類對象的VPtr是一樣的。
3、當基類指針指向派生類時,放生了強制轉換,基類的指針指向了派生類的VPtr,這樣當pBase->func()時,就可以調用派生類的func()了。
4、沒有虛函數的類也就沒有VTable表了,或者這個表為空。這樣基類指針自然調用不到派生類的函數了。
思考:
為何要讓父類指針指向派生類對象?
我覺得是為了實現C++的多態性。比如簡單工廠模式,一開始我們並不知道需要生產哪種產品,也就不知道返回的是什麽類型的產品,此時只能用基類指針去指向返回值。除此之外還沒想到其他的使用場景。
基類與派生類,父類指針指向子類對象