程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 淺談C++虛函數

淺談C++虛函數

編輯:C++入門知識

  很長時間都沒寫過博客了,主要是還沒有養成思考總結的習慣,今天來一發。     我是重度拖延症患者,本來這篇總結應該是早就應該寫下來的。   一、虛函數表     C++虛函數的機制想必大家都清楚了。不清楚的同學請參看各種C++入門書籍。這裡,我要討論一下這個虛函數機制究竟是怎麼實現的。虛函數主要是靠一張VTABLE來實現的,先來看看這個VTABLE在哪裡。     首先我們看下面的代碼:   復制代碼  1 class ClassA  2 {  3 public:  4     int m_data1;  5     int m_data2;  6     void vfunc1(){cout << "i am A" << endl;}  7 };  8 class ClassB : public ClassA  9 { 10 public: 11     int m_data3; 12     void funcB(){} 13     void vfunc1(){cout << "i am B" << endl;} 14 }; 15 class ClassC : public ClassB 16 { 17 public: 18     int m_data1; 19     int m_data4; 20     void funcC(){} 21     void vfunc1(){cout << "i am C" << endl;} 22 }; 23 int main() 24 { 25     ClassA a; 26     ClassB b; 27     ClassC c; 28     cout << sizeof(int) << endl; 29     cout << sizeof(ClassA) << endl; 30     cout << sizeof(ClassB) << endl; 31     cout << sizeof(ClassC) << endl; 32  33     cout << &(a) << endl; 34     cout << &(a.m_data1) <<endl; 35     cout << &(a.m_data2) <<endl; 36     cout << endl; 37  38     cout << &(b) << endl; 39     cout << &(b.ClassA::m_data1) << endl; 40     cout << &(b.ClassA::m_data2) << endl; 41     cout << &(b.m_data3) <<endl; 42     cout << endl; 43  44     cout << &(c) << endl; 45     cout << &(c.ClassA::m_data1) << endl; 46     cout << &(c.m_data2) << endl; 47     cout << &(c.m_data3) << endl; 48     cout << &(c.m_data1) <<endl; 49     cout << &(c.m_data4) <<endl; 50      51     return 0; 52 } 復制代碼   我如果把上面的程序中ClassA的函數vfunc1聲明成虛函數,即將第6行改為:   1 virtual void vfunc1(){cout << "i am A" << endl;}   程序運行的兩個結果分別為:         由上面的結果可以明顯的看出,聲明為虛函數的類比原來的類在大小上多了4個字節。沒有虛函數的類的起始地址和第一個成員變量的地址保持一致,有虛函數的類的起始地址在第一個成員變量地址的前四個字節。這中間多出來的這四個字節就是隱藏起來的VPTR。VPTR是一個指向一個VTABLE的指針,換句話說,這多出來的四個字節裡面存的是VTABLE的地址。     而VTABLE裡面就記錄了這個類裡面虛函數的地址。   再看下面的代碼:   1  ClassA *pa; 2  ClassB *pb; 3  ClassC *pc; 4  pa = &c; 5  pa->vfunc1();   我們都知道如果是虛函數,上面的代碼結果肯定為     i am C     如果沒用虛函數,結果為     i am A     這是怎麼做到的?     首先,我們要知道,子類繼承父類,子類擁有所有父類的成員變量跟成員函數,就是說:   1 c.vfunc1(); 2 c.ClassA::vfunc1()   我們可以上面的方式顯示地去訪問被子類覆蓋掉的函數和變量。可以理解為,雖然名字一樣,其實子類裡面有兩個獨立的vfunc1()函數,只不過子類調用的默認為ClassC::vfunc1()函數。     當我們用父類的指針去指向一個子類的指針時,會有一個向上轉換(我暫時這麼叫)的過程。用pa指向對象c時,pa是一個ClassA類型的指針,pa只能訪問ClassA類裡面有的成員變量和成員函數地址,多余的,A類沒有而C類有的成員變量和函數地址都被“upcasting”掉了。     沒有VTABLE時,只能找到ClassA類的vfunc1()函數的地址,找不到ClassC類的vfunc1()函數的地址。有虛函數表的存在時,對象c的虛函數表裡面會記錄ClassC::vfunc1()的地址,這樣用pa指向對象c時,虛函數表不會被“upcasting”掉,於是,按照虛函數表裡面的地址,就能夠成功訪問ClassC::vfunc1()。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved