前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >十个例子让你完全搞懂 C++ 的虚函数(不懂来找我)

十个例子让你完全搞懂 C++ 的虚函数(不懂来找我)

作者头像
Linux云计算网络
发布于 2018-01-11 02:22:40
发布于 2018-01-11 02:22:40
93000
代码可运行
举报
文章被收录于专栏:Linux云计算网络Linux云计算网络
运行总次数:0
代码可运行

Author: bakari  Date: 2012.4.8

虚函数是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

 例子代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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、多重继承(不含虚函数)(这个也没什么好说的)

直接看代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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个字节

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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、纯虚汗继承

(只在父类中申明,并在子类中实现申明的函数才可以用)

内存分配与前面只含虚函数的情况类似

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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 的虚函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 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很厉害,但对于文字的东西就不是很感冒,不过没关系,每个人有每个人的不同的需求,我是喜欢没事总喜欢写写的人,我想写的越多思路就不会堵塞

祝愿每一个朋友学习愉快,技术成精

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2012-08-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
浅析C++类的内存布局
之前Jungle写过一篇文章《探究C++:虚函数表究竟怎么回事?》,主要通过测试代码来验证虚函数表的存在,进而说明C++的多态机制。但完成文章后仍旧觉得文章云里雾里,并不能很好地说明C++类的内存布局。于是在阅读完3遍《深度探索C++对象模型》之后,重新整理了相关知识点,完成此文。
用户6557940
2022/07/24
5740
浅析C++类的内存布局
C++虚函数知识点总结
对应虚函数的类,该类的对象所占内存大小为,数据成员的大小+一个指向虚函数表指针 (4字节)。
半生瓜的blog
2023/05/12
2230
C++虚函数知识点总结
C++学习杂记
auto_ptr 是个 pointer-like 对象,也就是所谓的 “智能指针”,其析构函数会自动调用。
四火
2022/07/15
3000
C++学习杂记
C++基础知识复习
就算using namespace xxx了一个命名空间,我们仍然可以通过xxx::来使用其它的命名空间。
半生瓜的blog
2023/05/12
5720
C++基础知识复习
【c++】全面理解C++多态:虚函数表深度剖析与实践应用
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价
用户11029103
2024/05/24
3510
从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数、C++对象模型图
s1mba
2017/12/28
1.3K0
从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数、C++对象模型图
C++进阶:详解多态(多态、虚函数、抽象类以及虚函数原理详解)
注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承后在派生类依旧保持虚函数属性)但是该种写法不规范,大家还是少用为好。
是Nero哦
2024/03/17
6050
C++进阶:详解多态(多态、虚函数、抽象类以及虚函数原理详解)
【c++】多态&&虚函数&&抽象类&&继承中的虚函数表详解
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态
用户10925563
2024/06/04
3970
【c++】多态&&虚函数&&抽象类&&继承中的虚函数表详解
c++头脑风暴-多态、虚继承、多重继承内存布局
gdb怎么用这里就不展开了,默认你会使用gdb,使用gdb设置打印格式,然后看对象people的内存布局及大小,如下:
cpp加油站
2021/06/29
7380
图说C++对象模型:对象内存布局详解
0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局、虚表指针、虚基类指针等有深入了解的朋友可以慢慢看。 本文的结论都在VS2013上得到验证。不同的编译器在内存布局的细节上可能
Tencent JCoder
2018/07/02
4.4K0
【C++】多态 ⑧ ( 验证指向 虚函数表 的 vptr 指针 | 对比定义了虚函数的类和没有定义虚函数类的大小 )
" 虚函数表 " 由 C++ 编译器 负责 创建 与 维护 , 被 virtual 关键字 修饰的 虚函数 , 会自动 被 C++ 编译器 存储到 " 虚函数表 " 中 ;
韩曙亮
2023/11/01
2520
【C++】多态 ⑧ ( 验证指向 虚函数表 的 vptr 指针 | 对比定义了虚函数的类和没有定义虚函数类的大小 )
【C++】虚函数指针和虚函数列表
本篇文章主要来讲述,C++多态的实现原理,也就是虚函数和虚函数列表是怎么回事?它们是如何实现多态的?
灰子学技术
2020/04/02
1.5K0
从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响(类/对象的大小)
首先重新回顾一下关于类/对象大小的计算原则: 类大小计算遵循结构体对齐原则 第一个数据成员放在offset为0的位置 其它成员对齐至min(sizeof(member),#pragma pack(n
s1mba
2017/12/28
1.1K0
从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响(类/对象的大小)
【C++】看不懂多态?这篇文章带你吃透它!
​ 该内容中的代码以及解释都是在 vs2022 下的 x86 环境中,涉及的指针都是 4 个字节,如果要在其他的平台下运行,部分代码需要改动。
利刃大大
2025/02/03
710
【C++】看不懂多态?这篇文章带你吃透它!
C++ 虚拟继承
1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继 承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。实现的代码如 下: class A class B1:public virtual A; class B2:public virtual A; class D:public B1,public B2; 虚拟继承在一般的应用中很少用到,所以也往
猿人谷
2018/01/17
2.4K0
C++ 虚拟继承
深入探索虚函数表(详细)
这篇博客可能有一点点长,代码也有一点点多,但是仔细阅读分析完,会对虚函数表有一个深刻的认识。
Ch_Zaqdt
2020/03/13
1.3K0
深入探索虚函数表(详细)
适合具备 C 语言基础的 C++ 入门教程(六)
再上一则教程中,着重讲述了派生类继承于父类之后的一些访问控制,在本次教程中,将介绍如下几个点:派生类扩展父类功能,派生类的空间分布,以及多重继承的相关概念。
wenzid
2021/02/20
2510
适合具备 C 语言基础的 C++ 入门教程(六)
【C++】从零开始认识多态
面向对象技术(oop)的核心思想就是封装,继承和多态。通过之前的学习,我们了解了什么是封装,什么是继承。
叫我龙翔
2024/05/08
950
【C++】从零开始认识多态
C++多态
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态 举个例子:比如说买票,普通人是全价买,学生是半价,退伍军人是优先。
有礼貌的灰绅士
2023/04/06
2920
C++多态
C++多态
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
南桥
2024/08/07
1010
C++多态
推荐阅读
相关推荐
浅析C++类的内存布局
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档