多態(Polymorphism)按字面的意思就是“多種狀態”。在面向對象語言中,介面的多種不同的實現方式即為多態。引用Charlie Calverts對多態的描述——多態性是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作 ...
多態(Polymorphism)按字面的意思就是“多種狀態”。在面向對象語言中,介面的多種不同的實現方式即為多態。引用Charlie Calverts對多態的描述——多態性是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。其實我看到過一句話:調用同名函數卻會因上下文的不同而有不同的實現。我覺得這樣更加貼切,還加入了多態三要素:(1)相同函數名 (2)依據上下文 (3)實現卻不同;
多態是c++的特征之一
多態的分類:靜態多態(靜態聯編)、動態多態(動態聯編) 靜態多態(靜態聯編):函數入口地址 是在 編譯階段 確定(運算符重載、函數重載) 動態多態(動態聯編):函數入口地址 是在 運行階段 確定(虛函數)1 class Animal 2 { 3 public: 4 void sleep(void) 5 { 6 cout<<"animal 動物在睡覺"<<endl; 7 } 8 }; 9 10 class Cat:public Animal 11 { 12 public: 13 void sleep(void) 14 { 15 cout<<"Cat 貓在睡覺!! 喵喵"<<endl; 16 } 17 }; 18 void test01() 19 { 20 //用基類(指針或引用) 保存 子類對象(向上轉換) 21 Animal *p = new Cat; 22 p->sleep();//調用的是基類的sleep 23 24 Cat cat; 25 Animal &ob = cat; 26 ob.sleep();//調用的是基類的sleep 27 }
總結:基類指針、引用 只能訪問 子類對象中 基類部分 數據
3、使用基類指針、引用 訪問 子類對象中的成員方法(虛函數)
使用virtual修飾成員函數 該成員函數就是虛函數。
1 class Animal 2 { 3 public: 4 //虛函數 5 virtual void sleep(void) 6 { 7 cout<<"animal 動物在睡覺"<<endl; 8 } 9 };vfptr虛函數指針 指向的是虛函數表(vftable) vftable表存放的是 vfptr做保存的函數入口地址 註意://如果 Animal沒有涉及到繼承 函數指針變數 就指向自身sleep
1 class Animal 2 { 3 public: 4 //虛函數 本質 是一個函數指針變數 5 virtual void sleep(void) 6 { 7 cout<<"animal 動物在睡覺"<<endl; 8 } 9 }; 10 void test01() 11 { 12 //如果 Animal沒有涉及到繼承 函數指針變數 就指向自身sleep 13 Animal ob; 14 ob.sleep(); 15 }
4、擁有虛函數的類 涉及得到繼承 2-2
1 class Animal 2 { 3 public: 4 //虛函數 本質 是一個函數指針變數 5 virtual void sleep(void) 6 { 7 cout<<"animal 動物在睡覺"<<endl; 8 } 9 }; 10 class Cat:public Animal 11 { 12 public: 13 virtual void sleep(void) 14 { 15 cout<<"貓在睡覺!!喵喵"<<endl; 16 } 17 };
虛函數的定義要遵循以下重要規則:
1.如果虛函數在基類與派生類中出現,僅僅是名字相同,而形式參數不同,或者是返回類型不同,那麼即使加上了virtual關鍵字,也是不會進行滯後聯編的。
2.只有類的成員函數才能說明為虛函數,因為虛函數僅適合用與有繼承關係的類對象,所以普通函數不能說明為虛函數。
3.靜態成員函數不能是虛函數,因為靜態成員函數的特點是不受限制於某個對象。
4.內聯(inline)函數不能是虛函數,因為內聯函數不能在運行中動態確定位置。即使虛函數在類的內部定義定義,但是在編譯的時候系統仍然將它看做是非內聯的。
5.構造函數不能是虛函數,因為構造的時候,對象還是一片位定型的空間,只有構造完成後,對象才是具體類的實例。
6.析構函數可以是虛函數,而且通常聲名為虛函數。
同時需要瞭解多態的特性的virtual修飾,不單單對基類和派生類的普通成員 函數有必要,而且對於基類和派生類的析構函數同樣重要
在面試中經常會出現這個問題:C++的動態捆綁機制是怎麼樣的?
首先,我們看看編譯器如何處理虛函數。當編譯器發現我們的類中有虛函數的時候,編譯器會創建一張虛函數表,把虛函數的函數入口地址放到虛函數表中,並且在類中秘密增加一個指針,這個指針就是vpointer(縮寫vptr),這個指針是指向對象的虛函數表。在多態調用的時候,根據vptr指針,找到虛函數表來實現動態綁定。