函數重寫overwrite: 當子類提供了和父類同名的虛函數時,稱之為函數重寫,函數的返回值類 函數名 參數列表必須完全相同 名字隱藏namehide: 當子類提供了和父類同名的數據時 叫名字隱藏 函數重載: 同一個作用域中 函數名相同 參數列表不同的函數構成重載 多態 當父類型的指針(引用)指向子 ...
函數重寫overwrite:當子類提供了和父類同名的虛函數時,稱之為函數重寫,函數的返回值類 函數名 參數列表必須完全相同
名字隱藏namehide:當子類提供了和父類同名的數據時 叫名字隱藏
函數重載:同一個作用域中 函數名相同 參數列表不同的函數構成重載
多態
當父類型的指針(引用)指向子類對象時,通過父類型的指針 調用虛函數,如果子類重寫了這個虛函數,則調用的表現是子類的,否則就是父類型中對應的實現,多態使類型更加通用,根據具體的對象做出具體的行為
- 繼承是構成多態的基礎
- 虛函數是構成多態的關鍵
- 函數重寫是必備條件
應用
用在函數的參數上
void testAnimal(Dog* dog); //不好
void testAnimal(Animal* dog); //好
用在函數的返回值上
Cat* getAnimal(int s); //不好
Animal* getAnimal(int s); //好
靜態綁定:編譯時確定調用的函數地址
動態綁定:運行時確定調用函數的地址
虛表虛指針
多態的底層實現,靠的是虛函數表(虛表),
虛指針,指向虛表的指針任何一個具有虛函數的類型 只有一張虛表 同類型的對象共用虛函數表,當一個父類型的指針指向一個子類型的對象時,調用虛函數時並沒有立即生成函數的調用地址,而是先根據對象定位出對象的前4byte對象的虛函數表,再根據虛函數表中存放的函數地址進行調用,虛函數表中存放的是哪個函數,對應哪個類的實現,就調用相應的函數
類型識別
Q為什麼要有類型識別
A:多態做到的效果是類型通用,但這樣損失了對象的個性,有時候要恢復個性,根據具體的類型做出相應的個性展現
dynamic_cast<類型>(對象) //會嘗試著把對象變成相應的類型,如果成功就返回非空,不成功就返回NULL
typeid
這個運算符可以獲得類型或者對象的類型信息, typeid返回的信息存入一個type_info類型的對象中,這個類型重載==和!=運算符,並且有個成員函數name()返回類型的名稱
/usr/include/c++/4.6 下有一個頭文件#include
抽象類
不能實例化的類,除此之外和正常沒有區別,只要在類中出現一個純虛函數(pure),這個類就是抽象類
class A{
public:
virtual void show()=0; //純虛函數
};
Note:如果一個類繼承了抽象類,但沒有實現其中的純虛函數,那麼這個子類也會變成抽象類
虛析構函數
加了virtual修飾的析構函數,當父類對象的指針指向子類對象時,如果釋放指針對應的記憶體,只會調用父類對應的析構函數,子類析構行為未定義(不去調用)
如果把父類對應的析構函數改成虛析構,則會調用子類析構函數,同時由於子類的析構函數的調用必然會觸發父類析構函數的調用,上述問題得以解決
Q:什麼時候需要虛析構
A:當父類中有虛函數(此時常常需要用父類型對象指向子類型對象以實現多態)或者父子類型中都有堆記憶體處理,需要使用虛析構來釋放資源