十個例子讓你完全搞懂 C++ 的虛函數(不懂來找我)


 


我的公眾號 「Linux雲計算網絡」(id: cloud_dev),號內有 10T 書籍和視頻資源,后台回復 「1024」 即可領取,分享的內容包括但不限於 Linux、網絡、雲計算虛擬化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++編程技術等內容,歡迎大家關注。


Author: bakari  Date: 2012.4.8

裝載引用請注明出處:http://www.cnblogs.com/bakari/archive/2012/08/12/2635369.html   謝謝!

虛函數是C++中非常重要的一個概念,它最大的好處是能夠觸發動態綁定。C++中的函數默認不使用動態綁定,要觸發動態綁定,必須滿足 兩個條件:

第一,只有指定為虛函數的成員函數才能進行動態綁定,成員函數默認為非虛函數,非虛函數不進行動態綁定;

第二,必須通過基類類型的指針或引用進行函數的調用。具體理解指針或引用在使用繼承層次中某一類型的對象時會發生什么,本文不展開討論,

這兩天主要研習了虛函數的具體應用這一塊,而它的應用又非常廣泛,學MFC的應該能夠感受到它的強大,要說是總結也不一定能夠總結全,本人目前也處在studying中,所以用10個具體的例子來說明。例子是從難 到易,看到的朋友如果懂前面的可以不用看后面的。每一個例子就是一個類,通過類在內存中的布局來形象地分析虛函數究竟是如何運作的。圖表示可能抽象一點,一般帶有V開頭的表示一個虛函數表,如果是學過編譯原理這門課就很容易看懂,沒學過的只要懂虛函數的一些機制,耐着性子也是沒問題的。每個圖示都配有相應的代碼。可以對照着代碼來看。

1、  虛函數繼承的復雜例子

2、  菱形繼承無虛擬繼承的情況

3、  虛擬繼承的簡單情況

4、  單一普通繼承(無虛函數)

5、  單一繼承(含虛函數)(虛函數表只有一個)

6、  多重繼承(不含虛函數)

7、  多重繼承(一個含虛函數,一個不含虛函數)

8、  多重繼承(兩個都含有虛函數)

9、  純虛汗繼承

10、 private 的虛函數

 

1、虛函數繼承的復雜例子,見下圖:

見圖:左邊是這個類的內存布局,右邊是繼承圖示。 farther類和Uncle類都是虛擬繼承,其內部也都有偏移表,但我覺得這兩個表只是內部隱藏的,不在Son的內存布局中表示出來,本題Son的內存只有32個字節,如果表示出來就不止32個了,但是下面這個地方在內存中顯示是00 00 00 00,我猜想是不是GrandFather的偏移地址。

VbtSon(Father)

Farther~Num

VbtSon(Uncle)

Uncle~Num

Son~Num

Offset(這個地方??)

Vftable(GrandFather)

GrandFather~Num

                                        

 

 例子代碼:

 1 class GrandFather
 2 {
 3 public:
 4     GrandFather():i_G(5){cout<<"GrandFather() is called!"<<endl;}
 5     virtual ~GrandFather(){cout<<"~GrandFather() is called!"<<endl;}
 6 public:
 7     virtual void Test(){cout<<"GrandFather::Test() is called!"<<endl;}
 8 private:
 9     int i_G;
10 };
11 
12 class Father: virtual public GrandFather             //虛擬繼承
13 {
14 public:
15     Father():i_F(7){cout<<"Father() is called!"<<endl;};
16     virtual ~Father(){cout<<"~Father() is called!"<<endl;}
17 public:
18     virtual void Test(){cout<<"Father::Test() is called!"<<endl;}
19 private:
20     int i_F;
21 };
22 
23 class Uncle: virtual public GrandFather                          //虛擬繼承
24 {
25 public:
26     Uncle():i_U(3){cout<<"Uncle is called!"<<endl;}
27     virtual ~Uncle(){cout<<"~Uncle  is called!"<<endl;}
28 public:
29     virtual void Test(){cout<<"Uncle ::Test() is called!"<<endl;}
30 private:
31     int i_U;
32 };
33 
34 class Son:public Father,public Uncle                    
35 {
36 public:
37     Son():i_S(9){cout<<"Son is called!"<<endl;};
38     virtual ~Son(){cout<<"~Son  is called!"<<endl;}
39 public:
40     virtual void Test(){cout<<"Son ::Test() is called!"<<endl;}
41 private:
42     int i_S;
43 };
44 
45 int main(void)
46 {
47     Son p;
48     p.Test();
49     cout<<sizeof(Son)<<endl;
50     cout<<sizeof(Father)<<endl;
51     cout<<sizeof(GrandFather)<<endl;
52     return 0;
53 }

運行情況:

 

2、  菱形繼承無虛擬繼承的情況

VPTr1(Father)

GrandFarther~Num

Father~Num

VPtr(Uncle)

GrandFarther~Num

Uncle~Num

Son~Num

                         

 1 #include<iostream>
 2 using namespace std;
 3 class GrandFather
 4 {
 5 public:
 6     GrandFather():i_G(5){cout<<"GrandFather() is called!"<<endl;}
 7     virtual ~GrandFather(){cout<<"~GrandFather() is called!"<<endl;}
 8 public:
 9     virtual void Test(){cout<<"GrandFather::Test() is called!"<<endl;}
10 private:
11     int i_G;
12 };
13 class Father:   public GrandFather          //無虛擬繼承
14 {
15 public:
16     Father():i_F(7){cout<<"Father() is called!"<<endl;};
17     virtual ~Father(){cout<<"~Father() is called!"<<endl;}
18 public:
19     virtual void Test(){cout<<"Father::Test() is called!"<<endl;}
20 private:
21     int i_F;
22 };
23 
24 class Uncle:   public GrandFather             //無虛擬繼承
25 {
26 public:
27     Uncle():i_U(3){cout<<"Uncle is called!"<<endl;}
28     virtual ~Uncle(){cout<<"~Uncle  is called!"<<endl;}
29 public:
30     virtual void Test(){cout<<"Uncle ::Test() is called!"<<endl;}
31 private:
32     int i_U;
33 };
34 
35 class Son:public Father,public Uncle
36 {
37 public:
38     Son():i_S(9){cout<<"Son is called!"<<endl;};
39     virtual ~Son(){cout<<"~Son  is called!"<<endl;}
40 public:
41     virtual void Test(){cout<<"Son ::Test() is called!"<<endl;}
42 private:
43     int i_S;
44 };
45 
46 int main(void)
47 {
48     Son p;
49     p.Test();
50     cout<<sizeof(Son)<<endl;
51     cout<<sizeof(Father)<<endl;
52     cout<<sizeof(GrandFather)<<endl;
53     
54     return 0;
55 }

運行情況:

            

3、  虛擬繼承的簡單情況 見下圖:

VPTrD(A)              4      

Offset(A)              4

A~number             4

D~number             4

VPtr(Base)            4

Base~Number  

 

12 + 3cc + 4 = 40

                                

 1 class Base
 2 {
 3 public:
 4     Base(){strcpy_s(ch_rc,"abcdefg");}                  //初始化Base()::im
 5 public:
 6    virtual void Read(){cout<<"Base::Read()is called!"<<endl;}
 7 private:
 8     char ch_rc[12];
 9     bool ir;
10     int  im;                   
11 };
12 class A: virtual public Base     //虛擬繼承
13 {
14 public:
15     A():im_A(5){}                        //初始化A()::im_A
16 public:
17     virtual void Read(){cout<<"A::Read()is called!"<<endl;}
18 private:
19     int im_A;
20 };
21 class D:public A
22 {
23 public:
24     D():im_D(3){}          
25 public:
26     virtual void Read(){cout<<"D::Read()is called!"<<endl;}
27 private:
28     int im_D;
29 };
30 int _tmain(int argc, _TCHAR* argv[])
31 {
32     D obj;
33     cout<<sizeof(D)<<endl;
34     return 0;
35 }

運行情況:

 

4、單一普通繼承(無虛函數)(這個沒什么好說的)

                                                                                                      內存布局

Father~Number

Son~Number

 1 class Father
 2 {
 3 public:
 4     Father(){cout<<"Father() is called!"<<endl;}
 5     void TestF(const int &m){
 6         i_B=m;
 7         cout<<"Father::TestF() is called!"<<endl;
 8     }
 9     void Test(){cout<<"Base::Test() is called!"<<endl;}
10     ~Father(){cout<<"~Father() is called!"<<endl;}
11 private:
12     int i_B;
13 };
14  
15 class Son:public Father
16 {
17 public:
18     Son():i_A(5){cout<<"Son() is called!"<<endl;}
19      void Test(){cout<<"Son::Test() is called!"<<endl;}
20      ~Son(){cout<<"~Son() is called!"<<endl;}
21 private:
22     int i_A;
23 };
24 int main(int argc,char *argv[])
25 {
26     Father *p=new Son;
27     //Father *p=NULL;
28     p->Test();
29     delete p;
30     p=NULL;
31     cout<<sizeof(Son)<<endl;
32     return 0;
33 }

 

5、單一繼承(含虛函數)(虛函數表只有一個)見圖:

VPTr(father)

Father~number

Son~number

Child~number

                        

 1 #include<iostream>
 2 using namespace std;
 3 class Father
 4 {
 5 public:
 6     Father(){cout<<"Father() is called!"<<endl;}
 7     virtual  void Test(){cout<<"Base::Test() is called!"<<endl;}
 8     virtual ~Father(){cout<<"~Father() is called!"<<endl;}
 9 private:
10     int i_B;
11 };
12  
13 class Son:public Father
14 {
15 public:
16     Son():i_A(5){cout<<"Son() is called!"<<endl;}
17      void Test(){cout<<"Son::Test() is called!"<<endl;}
18      ~Son(){cout<<"~Son() is called!"<<endl;}
19 private:
20     int i_A;
21 };
22 int main(int argc,char *argv[])
23 {
24     Father *p=new Son;
25     //Father *p=NULL;
26     p->Test();
27     delete p;
28     p=NULL;
29     cout<<sizeof(Son)<<endl;
30     return 0;
31 }

運行情況:

 

6、多重繼承(不含虛函數)(這個也沒什么好說的)

直接看代碼:

 1 #include<iostream>
 2 using namespace std;
 3 class Father
 4 {
 5 public:
 6     Father():i_B(6){cout<<"Father() is called!"<<endl;}
 7      void TestF(const int &m){
 8         i_B=m;
 9         cout<<"Father::TestF() is called!"<<endl;
10     }
11       void Test(){cout<<"Father::Test() is called!"<<endl;}
12      ~Father(){cout<<"~Father() is called!"<<endl;}
13 private:
14     int i_B;
15 };
16 class Son
17 {
18 public:
19     Son():i_A(5){cout<<"Son() is called!"<<endl;}
20       void Test(){cout<<"Son::Test() is called!"<<endl;}
21       ~Son(){cout<<"~Son() is called!"<<endl;}
22 private:
23     int i_A;
24 };
25 
26 class Child:public  Father,public Son      //多重繼承
27 {
28 public:
29     Child():i_C(5){cout<<"Child() is called!"<<endl;}
30      void Test(){cout<<"Child::Test() is called!"<<endl;}
31      ~Child(){cout<<"~Child() is called!"<<endl;}
32 private:
33     int i_C;
34 };
35 int main(int argc,char *argv[])
36 {
37     Father *p=new Child;
38     //Father *p=NULL;
39     p->Test();
40     cout<<sizeof(Son)<<endl;
41     cout<<sizeof(Child)<<endl;
42     return 0;
43 }

 

7、多重繼承(一個含虛函數,一個不含虛函數)(類似單一繼承)

VPTr(father)

Father~number

Son~number

Child~number

                         

 1 #include<iostream>
 2 using namespace std;
 3 
 4 class Father
 5 {
 6 public:
 7     Father():i_B(6){cout<<"Father() is called!"<<endl;}
 8     virtual  void TestF(const int &m){
 9         i_B=m;
10         cout<<"Father::TestF() is called!"<<endl;
11     }
12       virtual void Test(){cout<<"Father::Test() is called!"<<endl;}
13      virtual  ~Father(){cout<<"~Father() is called!"<<endl;}
14 private:
15     int i_B;
16 };
17  
18 class Son
19 {
20 public:
21     Son():i_A(5){cout<<"Son() is called!"<<endl;}
22       void Test(){cout<<"Son::Test() is called!"<<endl;}
23       ~Son(){cout<<"~Son() is called!"<<endl;}
24 private:
25     int i_A;
26 };
27 
28 class Child:public  Father,public Son
29 {
30 public:
31     Child():i_C(5){cout<<"Child() is called!"<<endl;}
32      void Test(){cout<<"Child::Test() is called!"<<endl;}
33      ~Child(){cout<<"~Child() is called!"<<endl;}
34 private:
35     int i_C;
36 };
37 int main(int argc,char *argv[])
38 {
39     Father *p=new Child;
40     //Father *p=NULL;
41     p->Test();
42     cout<<sizeof(Son)<<endl;
43     cout<<sizeof(Child)<<endl;
44     return 0;
45 }

運行情況:

 

8、多重繼承(兩個都含有虛函數)

VPTr(father)

Father~number

VPTr(Son)

Son~number Child~number

 

20個字節

                         

 1 #include<iostream>
 2 using namespace std;
 3 
 4 class Father
 5 {
 6 public:
 7     Father():i_B(6){cout<<"Father() is called!"<<endl;}
 8     virtual  void TestF(const int &m){
 9         i_B=m;
10         cout<<"Father::TestF() is called!"<<endl;
11     }
12       virtual void Test(){cout<<"Father::Test() is called!"<<endl;}
13      virtual  ~Father(){cout<<"~Father() is called!"<<endl;}
14 private:
15     int i_B;
16 };
17 class Son
18 {
19 public:
20      Son():i_A(5){cout<<"Son() is called!"<<endl;}
21       virtual void Test(){cout<<"Son::Test() is called!"<<endl;}
22       virtual ~Son(){cout<<"~Son() is called!"<<endl;}
23 private:
24     int i_A;
25 };
26 
27 class Child:public  Father,public Son
28 {
29 public:
30     Child():i_C(7){cout<<"Child() is called!"<<endl;}
31      void Test(){cout<<"Child::Test() is called!"<<endl;}
32      ~Child(){cout<<"~Child() is called!"<<endl;}
33 private:
34     int i_C;
35 };
36 int main(int argc,char *argv[])
37 {
38     //Father *p=new Child;
39     Child p;
40     //Father *p=NULL;
41     p.Test();
42     cout<<sizeof(Son)<<endl;
43     cout<<sizeof(Child)<<endl;
44     return 0;
45 }

運行情況:

 

9、純虛汗繼承

(只在父類中申明,並在子類中實現申明的函數才可以用)

內存分配與前面只含虛函數的情況類似

 1 #include<iostream>
 2 using namespace std;
 3 class A
 4 {
 5 public:
 6     A():i_A(5){cout<<"A() is called!"<<endl;}
 7 public:
 8 virtual void Test()= 0; //prue virtual function
 9 virtual void Base() {cout<<"this is farther class"<<endl;}
10 private:
11     int i_A;
12 };
13 
14 class B:public A
15 {
16 public:
17     B():i_B(9){}
18 public:
19     void Test() { cout<<" this is SubVirtual!"<<endl;}                          //必須在子類中實現該函數才可以用
20 void Base() { 
21 cout<<"this is subclass Base"<<endl;
22 }
23 private:
24     int i_B;
25 };
26 
27 int  main(void)
28 {
29     A* p = new B; //multstate pointer
30     p->Test();
31     p->Base();
32     cout<<sizeof(B)<<endl;
33     return 0 ;
34 }

 

10、private 的虛函數

 1 #include<iostream>
 2 using namespace std;
 3 
 4 class A
 5 {
 6 public:
 7 virtual void Test() { func();}
 8 private:
 9     int i_A;
10     virtual void func() {cout<<"A::func () is Called!"<<endl; }
11 };
12 class B: public A
13 {
14 private:
15 //雖然func()在A類中是private的,但是仍然可以出現在派生類中,並仍然可以與public或者protected的虛函數一樣產生多態的效果。
16     virtual void func() { cout<<"B::func() is Called!"<<endl;} private:
17     int i_B;
18 };
19 
20 int main(void)
21 {
22     //A *p=new B;
23     A p;
24     //B p;
25     //p->func();
26     p.Test();
27     cout<<sizeof(B)<<endl;
28     return 0;
29 }

運行情況:

 

 

OK,做這個東西很辛苦,沒辦法,搞技術的就得這樣。

寫這些程序然后進行測試是很輕松的一件事,然而要把這些東西以文字的方式整理出來,的確不是一件容易的事。所以,就有很多IT高手,技術流,Coding很厲害,但對於文字的東西就不是很感冒,不過沒關系,每個人有每個人的不同的需求,我是喜歡沒事總喜歡寫寫的人,我想寫的越多思路就不會堵塞

祝願每一個朋友學習愉快,技術成精!

每一個人的辛苦都是值得肯定的,裝載引用請注明出處:http://www.cnblogs.com/bakari/archive/2012/08/12/2635369.html  謝謝!

 


我的公眾號 「Linux雲計算網絡」(id: cloud_dev),號內有 10T 書籍和視頻資源,后台回復 「1024」 即可領取,分享的內容包括但不限於 Linux、網絡、雲計算虛擬化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++編程技術等內容,歡迎大家關注。


免責聲明!

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



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