C++虛函數與虛函數表

一、多態是C++三大特性之一,也是面向對象設計中一個很是重要的概念。所謂多態性就是當不一樣的html

對象接收到相同的消息時所產生的不一樣的響應。ios

C++中虛函數的存在其實就是爲了解決面向對象編程設計當中的多態問題,即經過基類的指針(或者是引用)編程

指向實例化的派生類對象,從而經過基類的指針(或者是引用)調用派生類的成員函數,從而實現晚綁定(函數

不在編譯時根據函數名和參數來肯定調用哪個函數,而是在運行時根據具體的環境來動態的肯定)。spa

正是因爲虛函數這種機制的存在,把基類的析構函數申明爲虛析構函數就可以解決經過釋放基類指針所形成的設計

內存泄露的問題。指針

 

二、沒有繼承關係時的虛函數表code

 1 #include <iostream>
 2 
 3 class CBassClass {
 4 public:
 5     virtual void FunC1(void) { std::cout << "FunC1(void)" << std::endl; }
 6     virtual void FunC2(void) { std::cout << "FunC2(void)" << std::endl; }
 7     virtual void FunC3(void) { std::cout << "FunC3(void)" << std::endl; }
 8 };
 9 
10 typedef void (*pfnFun) (void);
11 
12 int main(void)
13 {    
14     CBassClass *pBase = new CBassClass;
15 
16     std::cout << "虛函數表地址:0x" << (int *)pBase << std::endl;          // 虛函數表地址存在對象所佔內存空間的前4個字節
17     std::cout << "虛函數表中的第一個函數地址:0x" << *((int *)(*(int *)pBase) + 0) << std::endl;
18     std::cout << "虛函數表中的第二個函數地址:0x" << *((int *)(*(int *)pBase) + 1) << std::endl;
19     std::cout << "虛函數表中的第三個函數地址:0x" << *((int *)(*(int *)pBase) + 2) << std::endl;
20     std::cout << "虛函數表中的結束標誌:0x" << *((int *)(*(int *)pBase) + 3) << std::endl;
21 
22     pfnFun fn = NULL;
23     for (int i = 0; i < 3; i++) {
24         fn = (pfnFun)*((int *)(*(int *)pBase) + i);
25         (*fn)();
26     }
27 
28     delete pBase;
29     return 0;
30 }

以上代碼的輸出結果:htm

   

從結果能夠看出來,虛函數的入口地址在虛函數表當中是按照順序依次存放的,最後會以0做爲結束標誌。對象

 

三、存在繼承關係的虛函數表(不存在覆蓋關係時)

 1 #include <iostream>
 2 
 3 class CBassClass {
 4 public:
 5     virtual void FunC1(void) { std::cout << "CBassClass FunC1(void)" << std::endl; }
 6     virtual void FunC2(void) { std::cout << "CBassClass FunC2(void)" << std::endl; }
 7     virtual void FunC3(void) { std::cout << "CBassClass FunC3(void)" << std::endl; }
 8 };
 9 
10 class DClass :public CBassClass {
11 public:
12     virtual void FunC4(void) { std::cout << "DClass FunC4(void)" << std::endl; }
13     virtual void FunC5(void) { std::cout << "DClass FunC5(void)" << std::endl; }
14 };
15 
16 typedef void (*pfnFun) (void);
17 
18 int main(void)
19 {    
20     CBassClass *pBase = new DClass;    // DClass *pBase = new DClass; 結果同樣
21 
22     std::cout << "虛函數表地址:0x" << (int *)pBase << std::endl;    // 虛函數表地址存在對象所佔內存空間的前4個字節
23     std::cout << "虛函數表中的第一個函數地址:0x" << *((int *)(*(int *)pBase) + 0) << std::endl;
24     std::cout << "虛函數表中的第二個函數地址:0x" << *((int *)(*(int *)pBase) + 1) << std::endl;
25     std::cout << "虛函數表中的第三個函數地址:0x" << *((int *)(*(int *)pBase) + 2) << std::endl;
26     std::cout << "虛函數表中的第四個函數地址:0x" << *((int *)(*(int *)pBase) + 3) << std::endl;
27     std::cout << "虛函數表中的第五個函數地址:0x" << *((int *)(*(int *)pBase) + 4) << std::endl;
28     std::cout << "虛函數表中的結束標誌:0x" << *((int *)(*(int *)pBase) + 5) << std::endl;
29 
30     pfnFun fn = NULL;
31     for (int i = 0; i < 5; i++) {
32         fn = (pfnFun)*((int *)(*(int *)pBase) + i);
33         (*fn)();
34     }
35 
36     delete pBase;
37 
38     return 0;
39 }

以上代碼運行的結果:

 1 虛函數表地址:0x0052B1C0
 2 虛函數表中的第一個函數地址:0x16520113
 3 虛函數表中的第二個函數地址:0x16520378
 4 虛函數表中的第三個函數地址:0x16520383
 5 虛函數表中的第四個函數地址:0x16520423
 6 虛函數表中的第五個函數地址:0x16520418
 7 虛函數表中的結束標誌:0x0
 8 CBassClass FunC1(void)
 9 CBassClass FunC2(void)
10 CBassClass FunC3(void)
11 DClass FunC4(void)
12 DClass FunC5(void)

從上面的結果能夠看出來,虛函數表中的函數地址是按順序存放的,派生類繼承了基類的虛函數表,而後

又在虛函數表中將本身的虛函數的函數地址存放在虛函數表中,先存放基類的虛函數後存放派生類的虛函數。

 

四、存在繼承關係的虛函數表(存在覆蓋關係時)

 1 #include <iostream>
 2 
 3 class CBassClass {
 4 public:
 5     virtual void FunC1(void) { std::cout << "CBassClass FunC1(void)" << std::endl; }
 6     virtual void FunC2(void) { std::cout << "CBassClass FunC2(void)" << std::endl; }
 7     virtual void FunC3(void) { std::cout << "CBassClass FunC3(void)" << std::endl; }
 8 };
 9 
10 class DClass :public CBassClass {
11 public:
12     virtual void FunC2(void) { std::cout << "DClass FunC2(void)" << std::endl; }
13     virtual void FunC3(void) { std::cout << "DClass FunC3(void)" << std::endl; }
14     virtual void FunC4(void) { std::cout << "DClass FunC4(void)" << std::endl; }
15     virtual void FunC5(void) { std::cout << "DClass FunC5(void)" << std::endl; }
16 };
17 
18 typedef void (*pfnFun) (void);
19 
20 
21 int main(void)
22 {    
23     CBassClass *pBase = new DClass;
24 
25     std::cout << "虛函數表地址:0x" << (int *)pBase << std::endl;    // 虛函數表地址存在對象所佔內存空間的前4個字節
26     std::cout << "虛函數表中的第一個函數地址:0x" << *((int *)(*(int *)pBase) + 0) << std::endl;
27     std::cout << "虛函數表中的第二個函數地址:0x" << *((int *)(*(int *)pBase) + 1) << std::endl;
28     std::cout << "虛函數表中的第三個函數地址:0x" << *((int *)(*(int *)pBase) + 2) << std::endl;
29     std::cout << "虛函數表中的第四個函數地址:0x" << *((int *)(*(int *)pBase) + 3) << std::endl;
30     std::cout << "虛函數表中的第五個函數地址:0x" << *((int *)(*(int *)pBase) + 4) << std::endl;
31     std::cout << "虛函數表中的結束標誌:0x" << *((int *)(*(int *)pBase) + 5) << std::endl;
32 
33     pfnFun fn = NULL;
34     for (int i = 0; i < 5; i++) {
35         fn = (pfnFun)*((int *)(*(int *)pBase) + i);
36         (*fn)();
37     }
38 
39     delete pBase;
40 
41     return 0;
42 }

以上代碼運行的結果是:

 1 虛函數表地址:0x006CB1C0
 2 虛函數表中的第一個函數地址:0x267185
 3 虛函數表中的第一個函數地址:0x267510
 4 虛函數表中的第一個函數地址:0x267505
 5 虛函數表中的第一個函數地址:0x267495
 6 虛函數表中的第一個函數地址:0x267490
 7 CBassClass FunC1(void)
 8 DClass FunC2(void)
 9 DClass FunC3(void)
10 DClass FunC4(void)
11 DClass FunC5(void)

從結果能夠看出來,派生類中重寫了基類中的兩個虛函數,派生類繼承了基類的虛函數表以後,將虛函數表中

相應的兩個函數指針替換成了派生類本身的虛函數。其餘的卻是沒什麼變化。

 

五、多繼承狀況

示例代碼就不拷貝出來了,總之就是多重繼承的狀況,會爲每個基類建一個虛函數表。派生類的虛函數放到

第一個虛函數表的後面。

具體的請看這篇博客:  http://www.cnblogs.com/chinazhangjie/archive/2012/07/11/2586535.html 

相關文章
相關標籤/搜索