C++多态底层原理:虚函数表
虚函数表
C++ 对象模型
在有虚函数的情况下,C++对象的模型可以概括为:虚函数表指针+数据struct。在对象所在的内存里:前8个字节(64位系统)是虚函数表所在地址,后边是对象中的member data。在多态的实现里,父指针就是根据所指向内存里的第一个地址来找到对应的虚函数表从而实现多态。

可以用以下程序验证C++内存模型:
void show_bits(void *ptr, int len)
{
unsigned char *p = (unsigned char*) ptr;
for(int i = len-1; i >= 0; --i)
printf("%x", p[i]);
printf("\n");
}
class A{
public:
virtual void f1(){}
virtual void f2(){}
};
class B:public A {
public:
int a = -1;
void f1(){ cout << "B f1\n"; }
void f2(){ cout << "B f2\n"; }
};
using FUN = void(*)();
int main()
{
B b;
// 虚函数表数组的地址8个字节,加上int a的4个字节
show_bits(&b, 12); // 输出:ffffffff000128c18
// b.a 的4个字节
show_bits(&b.a, 4); // 输出:ffffffff,0x000128c18指向的就是虚函数表
cout << sizeof(b) << endl; // 输出16,因为是64位系统,所以12B会内存对齐到16B
//得到虚函数表数组地址
void **vtbl = *(void***)&b;
// 虚函数表数组第一个元素就是第一个虚函数f1的地址
((FUN)(vtbl[0]))(); //输出:B f1
// 虚函数表数组第一个元素就是第一个虚函数f2的地址
((FUN)(vtbl[1]))(); //输出:B f2
B c;
show_bits(&c, 12); // 输出:ffffffff000128c18,说明同一个类的不同对象指向同一个虚函数表
return 0;
}
以下程序验证多态:
class A{
public:
virtual void f1(){ cout << "A f1\n"; }
virtual void f2(){ cout << "A f2\n"; }
};
class B:public A {
public:
void f1(){ cout << "B f1\n"; }
void f2(){ cout << "B f2\n"; }
};
using FUN = void(*)();
// 多态
void f(A& a) {
void **vtbl = *(void***)&a;
cout << "虚函数表地址:" << (void*)vtbl << endl;
((FUN)(vtbl[0]))();
}
int main()
{
A a1, a2;
B b1, b2;
f(a1);
f(a2);
f(b1);
f(b2);
return 0;
}
/* 输出:
虚函数表地址:0x100a14108
A f1
虚函数表地址:0x100a14108
A f1
虚函数表地址:0x100a14138
B f1
虚函数表地址:0x100a14138
B f1
*/
多重继承
与单继承的C++对象模型类似,只不过从几个类继承,就有几个虚函数表:

以下程序验证:
class A{
public:
int a;
virtual void f1(){ cout << "A f1\n"; }
virtual void f2(){ cout << "A f2\n"; }
};
class B {
public:
int b;
virtual void f1(){ cout << "B f1\n"; }
virtual void f2(){ cout << "B f2\n"; }
virtual void f3() {cout << "B f3\n"; }
};
class C: public A, public B {
public:
// void f1() { cout << "C f1\n"; }
// void f2() { cout << "C f2\n"; }
// void f3() { cout << "C f3\n"; }
};
using FUN = void(*)();
void f(A& a) {
void ***vptr = (void***)&a;
cout << "A虚函数表地址:" << (void*)vptr[0] << endl;
// 因为A虚函数表后边还有一个int a,vptr+1是int a的地址
// 所以vptr+2才是B的虚函数表地址(64位系统内存对齐)
cout << "B虚函数表地址:" << (void*)vptr[2] << endl;
((FUN)vptr[0][0])();
((FUN)vptr[0][1])();
((FUN)vptr[2][0])();
((FUN)vptr[2][1])();
((FUN)vptr[2][2])();
/*输出:
A虚函数表地址:0x104bdc110
B虚函数表地址:0x104bdc130
A f1
A f2
B f1
B f2
B f3
*/
}
int main()
{
C c;
f(c);
return 0;
}
C++多态底层原理:虚函数表的更多相关文章
- C++ 虚函数表与多态 —— 多重继承的虚函数表 & 内存布局
多重继承的虚函数表会有两个虚表指针,分别指向两个虚函数表,如下代码中的 vptr_s_1.vptr_s_2,Son类继承自 Father 和 Mather 类,并且改写了 Father::func_1 ...
- C++ 虚函数表与多态 —— 继承的虚函数表 & 内存布局
1. 使用继承的虚函数表: 如果不涉及多重继承,每个类只有1个虚函数表,当子类继承父类后,子类可以自己改写和新增虚函数,如下图所示: 子类重写 func_1 后,子函数的 func_1 将会有新的逻辑 ...
- 虚函数表-C++多态的实现原理
目录 1.说明 2.虚函数表 3.代码示例 参考:http://c.biancheng.net/view/267.html 1.说明 我们都知道多态指的是父类的指针在运行中指向子类,那么它的实现原理是 ...
- C++中的虚函数以及虚函数表
一.虚函数的定义 被virtual关键字修饰的成员函数,目的是为了实现多态 ps: 关于多态[接口和实现分离,父类指针指向子类的实例,然后通过父类指针调用子类的成员函数,这样可以让父类指针拥有多种形态 ...
- 深入剖析C++多态、VPTR指针、虚函数表
在讲多态之前,我们先来说说关于多态的一个基石------类型兼容性原则. 一.背景知识 1.类型兼容性原则 类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代.通过公有继承,派 ...
- C++ 虚函数表与多态 —— 虚函数表的内存布局
C++面试经常会被问的问题就是多态原理.如果对C++面向对象本质理解不是特别好,问到这里就会崩. 下面从基本到原理,详细说说多态的实现:虚函数 & 虚函数表. 1. 多态的本质: 形 ...
- 从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数
一.多态 多态性是面向对象程序设计的重要特征之一. 多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为. 多态的实现: 函数重载 运算符重载 模板 虚函数 (1).静态绑定与动态绑 ...
- C++虚函数表原理
C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指 向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有“多种形态”,这是一种泛型技 ...
- 类虚函数表原理实现分析(当我们将虚表地址[n]中的函数替换,那么虚函数的实现就由我们来控制了)
原理分析 当调用一个虚函数时, 编译器生成的代码会调用 虚表地址[0](param1, param2)这样的函数. 已经不是在调用函数名了. 当我们将虚表地址[n]中的函数实现改为另外的函数, 虚函数 ...
- C++多态,虚函数,虚函数表,纯虚函数
1.多态性 指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作. C++支持两种多态性:编译时多态性,运行时多态性. a.编译时多态性:通过重载函数实现 ,模板(2次编译) ...
随机推荐
- 【算法】priority_queue在力扣题中的应用 | 力扣692 | 力扣347 | 力扣295 【超详细的注释和算法解释】
说在前面的话 博主也好长一段时间没有更新力扣的刷题系列了,今天给大家带来一些优先队列的经典题目,今天博主还是用C++给大家讲解,希望大家可以从中学到一些东西. 前言 那么这里博主先安利一下一些干货满满 ...
- 答疑解惑:解释在Mybatis-Spring下@Mapper与@MapperScan为何不能同时生效以及实现动态条件注册Mapper接口
若项目中使用了@MapperScan注解后,则@Mapper注解不再生效, 原因是:@MapperScan注解 会执行@Import(MapperScannerRegistrar.class),而Ma ...
- YOLO数据集划分(测试集和验证集)
在目标检测任务中,数据集的划分通常分为训练集和验证集,以便在训练模型时评估模型的性能.这个过程对于有效训练和评估目标检测模型非常重要.下面是划分目标检测数据集的一般步骤:`` 数据集组织: 确保你的数 ...
- Slot 的含义
Slot 解释 1)slot就是槽的意思,是一个资源单位,只有给task分配了一个slot之后,这个task才可以运行.slot分两种,map slot沪蓉reduce slot.另外,slot是一个 ...
- comm命令
comm命令 comm命令用于比较两个已排过序的文件,该命令会一列列地比较两个已排序文件的差异,并将其结果显示出来,如果没有指定任何参数,则会把结果分成3列显示:第1列仅是在第1个文件中出现过的列,第 ...
- colrm命令
colrm命令 colrm命令用于编辑源代码文件,脚本文件或常规文本文件中的文本,此命令从文件中删除选定的列,列定义为一行中的单个字符.索引总是从1开始,而不是0.如果同时指定了开始和结束,则它们之间 ...
- SpringBoot相关注解使用
@MapperScan注解和@Mapper @Mapper常用在相应的 *Mapper类上,但是每个类都使用相对麻烦,所以后来用@MapperScan替代 ,@MapperScan注解使用在启动类上. ...
- 我的小程序之旅六:微信公众号授权登录(适用于H5小程序)
实现步骤 1 第一步:用户同意授权,获取code 2 第二步:通过code换取网页授权access_token 3 第三步:刷新access_token(如果需要) 4 第四步:拉取用户信息(需sco ...
- IDA 常用快捷键记录
常用快捷键1 1.切换文本视图与图表视图 空格键 2.返回上一个操作地址 ESC 3.搜索地址和符号 G 4.对符号进行重命名 N 5.常规注释 冒号键 6.可重复注释 分号键 7.添加标签 Alt+ ...
- cookie和localStorage和sessionStorage的区别
cookie和localStorage和sessionStorage的区别 下面从几个方向区分一下 cookie,localStorage,sessionStorage 的区别 生命周期: cooki ...