C++单继承、多继承情况下的虚函数表分析

C++的三大特性之一的多态是基于虚函数实现的,而大部分编译器是采用虚函数表来实现虚函数,虚函数表(VTAB)存在于可执行文件的只读数据段中,指向VTAB的虚表指针(VPTR)是包含在类的每一个实例当中。当使用引用或指针调用虚函数时,首先通过VPTR找到VTAB,然后通过偏移量找到虚函数地址并调用。

 

本文参考:1.http://blog.lucode.net/programming-language/cpp-vtab-and-call-convention.html

  2.https://blog.csdn.net/tangaowen/article/details/5830803

  3.《深度探索C++对象模型》

一、单继承

 1 #include<iostream>
 2 #include <stdio.h>
 3 using namespace std;
 4 class A {
 5 public:
 6     void func() {
 7         cout << "A::func()" << endl;
 8     }
 9     virtual void func1() {
10         cout << "A::func1(): " << endl;
11     }
12 
13     virtual void func3() {
14         cout << "A::func3(): " << endl;
15     }
16 };
17 
18 class B: public A {
19 public:
20     virtual void func() {
21         cout << "B::func()" << endl;
22     }
23     virtual void vfunc() {
24         cout << "B::vfunc()" << endl;
25     }
26     void func1() {
27         cout << "B::func1(): " << endl;
28     }
29 };
30 int main() {
31     typedef void (*Fun)(void);
32     B a;
33 
34     Fun *fun = NULL;
35     fun = (Fun*) ((int *) *(int *) &a);
36 //    fun = *(Fun **) &a;
37     fun[0]();
38     fun[1]();
39     fun[2]();
40     fun[3]();
41 
42     return 0;
43 }

运行结果:

B::func1():
A::func3():
B::func()
B::vfunc()

二、多重继承

 1 #include<iostream>
 2 #include <stdio.h>
 3 using namespace std;
 4 class B1 {
 5 public:
 6     virtual void barB1() {cout << "B1::bar" << endl;}
 7     virtual void fooB1() {cout << "B1::foo" << endl;}
 8 };
 9 
10 class B2 {
11 public:
12     virtual void barB2() {cout << "B2::bar" << endl;}
13     virtual void fooB2() {cout << "B2::foo" << endl;}
14 };
15 
16 class D : public B1, B2 {
17 public:
18     void fooB1() {cout << "D::foo" << endl;}
19     void barB2() {cout << "D::bar" << endl;}
20 };
21 
22 typedef void (*Func)();
23 int main() {
24     D tt;
25     Func* vptr1 = *(Func**)&tt;
26     Func* vptr2 = *((Func**)&tt + 1);
27 
28     vptr1[0]();
29     vptr1[1]();
30     vptr1[2]();
31     cout<<"\\\\\\\\\\\\"<<endl;
32     vptr2[0]();
33     vptr2[1]();
34 
35     return 0;
36 }

运行结果:

B1::bar
D::foo
D::bar
\\\\\\
D::bar
B2::foo

结论:

     多重继承会有多个虚函数表,几重继承,就会有几个虚函数表。这些表按照派生的顺序依次排列,如果子类改写了父类的虚函数,那么就会用子类自己的虚函数覆盖虚函数表的相应的位置,如果子类有新的虚函数,那么就添加到第一个虚函数表的末尾。

  

再简单总结一下 覆盖 隐藏 重载 的区别:

覆盖 是C++虚函数的实现原理,基类的虚函数被子类重写,要求函数参数列表相同;

隐藏 是C++的名字解析过程,分两种情况,基类函数有virtual,参数列表不同,或基类函数没有virtual,无论参数列表是否相同。此时基类指针指向基类实例则调用基类函数,指向子类则调用子类函数。

重载 是在同一命名空间中根据参数对同名函数的区别。

 

 

// 我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3ph7kzdx2saoo

转载于:https://www.cnblogs.com/bobojiang/p/11282446.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多继承下,虚函数的设计确实存在一些问题。由于一个子类可以继承多个父类,每个父类都有自己的虚函数,这就导致了可能存在多个虚函数情况。这样一来,就需要有一种机制来确定使用哪个虚函数来处理相应的虚函数调用。 一个常见的解决方案是使用虚函数指针(vptr)来指向正确的虚函数。子类的对象会包含一个或多个虚函数指针,每个指针对应一个父类的虚函数。当调用子类对象的虚函数时,编译器会根据具体的函数调用来选择正确的虚函数指针,从而找到对应的虚函数进行调用。 然而,多继承下的虚函数指针的布局和处理并不像继承那么简。由于可能存在多个虚函数指针,它们的排列顺序以及内存布局都会受到编译器和操作系统的影响。这就导致了多继承虚函数的布局可能会比较复杂,不同编译器和操作系统可能会有不同的实现方式。 另外,多继承下还存在一个问题是菱形继承(Diamond Inheritance)导致的虚函数冗余。菱形继承是指一个子类同时继承了两个间接父类,并且这两个父类又继承了同一个基类。在这种情况下,子类会继承两份相同的虚函数,其中一份是冗余的。为了解决这个问题,C++中引入了虚函数指针的偏移来消除虚函数的冗余。 综上所述,在多继承下,虚函数存在布局复杂性和虚函数冗余的问题。具体的虚函数布局和处理方式会受到编译器和操作系统的影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值