C++多態實現機制


多態是OOP中一個十分重要的特性,至於如何使用可以參考這篇C++與JAVA多態相同與不同,相信對於學習C++和java的人都有幫助。

多態實現的關鍵技術是動態綁定。

動態綁定:程序在運行期間尋找函數地址

靜態綁定:代碼在編譯時已經確定了函數地址

 

多態的實現表象是指針+虛函數,本質是虛表+虛指針。

這里有一篇博客寫的很全面,可以參考一下c++多態實現的機制

 

1.虛表

聲明了虛函數的類會隱式創建一個虛指針指向虛表,而虛表保存了虛函數的地址(虛表中只有虛函數,非虛函數在另一個表中),當我們調用虛函數時,實際上是通過虛指針去查找虛表中對應虛函數的地址

2.虛指針

虛指針指向類的虛表,屬於類的成員變量,可以繼承,當子類繼承父類的虛指針時,子類的虛指針指向子類的虛表。虛表也可以繼承,也可以理解為繼承了虛函數,然后創建了自己的虛表,所以如果沒有重定義父類虛函數,那么子類虛表和父類虛表完全相同;重定義的虛函數在子類虛表中的地址會改變。

3.虛指針的初始化

虛指針在構造函數中進行初始化,指向類的虛表。如果類B繼承了類A,那么我們知道生成B的實例對象時,會先調用A的構造函數,此時虛指針指向A的虛表,然后調用B的構造函數,此時虛指針指向B的虛表,所以當B的實例構造完成后,虛指針指向B的虛表。

4.多態

用下面的例子講解,當我們聲明一個父類A的指針p指向子類B的對象時,在編譯階段是不分配內存的(不構造對象的),如果想了解編譯過程做了什么,可以參考一下這篇C程序編譯過程淺析。也就是說編譯器並不知道指針p指向的是B類的對象,只知道這是一個A類的指針,那么在編譯p->func()時,會直接查找A中func的地址並替換(可以簡單理解為這樣),也就是靜態綁定,那么在運行時自然調用的就是A的func了

 1 class A
 2 {
 3 public:
 4     A(){};
 5     void func()
 6     {
 7         cout << "A func called!";
 8     }
 9 };
10 
11 class B:public A
12 {
13 public:
14     B(){};
15     void func()
16     {
17         cout << "B func called!";
18     }
19 
20 };
21 
22 int main()
23 {
24     A*p = new B();
25     p->func();
26     return 0;
27 }

 

但是如果將func聲明為虛函數,如下,那么在編譯時編譯器一看func是虛函數,會直接跳過,那么在運行時,B的對象已經被構造出來了,那么p所指向的B對象的虛指針已經指向了B的虛表,此時調用p->func()時,操作系統會在p的虛表中去尋找func這個函數,也就是動態綁定,然后調用,這時候調用的自然就是B的func了,可以說虛指針和虛表就是為了多態而生的。

 1 class A
 2 {
 3 public:
 4     A(){};
 5     virtual  void func()
 6     {
 7         cout << "A func called!";
 8     }
 9 };
10 
11 class B:public A
12 {
13 public:
14     B(){};
15     void func()
16     {
17         cout << "B func called!";
18     }
19 
20 };
21 
22 int main()
23 {
24     A*p = new B();
25     p->func();
26     return 0;
27 }

 

看到這里,大家再去看我寫的C++與JAVA多態相同與不同,也就明白為什么我說重寫和重定義不同了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM