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次编译) ...
随机推荐
- [奶奶看了都会]ChatGPT接入企业微信成为聊天机器人
1.聊天效果 上次给大家讲了ChatGPT接入个人微信的方法,但是个人微信容易被封号.这次就教大家接入企业微信,不会再被封号哦~ 话不多说,直接看机器人的聊天效果.基本能实现ChatGPT的聊天效果了 ...
- Oracle删除索引规范
1.背景概述 2.索引删除规范 3.根本解决方案及建议 1.背景概述 近期应用升级上线过程中,存在删除业务表索引的变更操作,且因删除索引导致次日业务高峰时期,数据库响应缓慢的情况,经定位是缺失索引导致 ...
- 《ASP.NET Core 与 RESTful API 开发实战》-- (第8章)-- 读书笔记(尾)
第 8 章 认证和安全 配置数据保护 在默认的情况下,数据保护 API 有自身的默认配置,如密钥的保存位置.密钥的有效期.所使用的算法等 前面已经提到了密钥的默认有效期以及用到的算法,对于密钥的保存位 ...
- 当我们在谈 .NET Core 跨平台时,我们在谈些什么?--学习笔记
摘要 .NET Framework在过去十多年在跨平台上的尝试. .NET Core跨平台的实现有何不同? 基于 .NET Standard的平台兼容性是如何实现的? 讲师介绍 历史枷锁 .NET F ...
- C语言程序设计之 循环控制2020-10-20
2020-10-20 整理: 第一题: 2011年开始实行新个人所得税法,要求输入月薪salary,输出应交的个人所得税 tax (保留两位小数). 新税法方案如下: tax=rate*(salary ...
- 开源.NetCore通用工具库Xmtool使用连载 - 发送短信篇
[Github源码] <上一篇> 介绍了Xmtool工具库中的发送邮件类库,今天我们继续为大家介绍其中的发送短信类库. 发送短信就像发送邮件一样,在软件系统中使用非常普遍,甚至比发送邮件还 ...
- MYSQL 3 DAY
目录 MySQL day03 1.约束 1.1.唯一性约束(unique) 1.2.主键约束 1.3.外键约束 2.存储引擎?(整个内容属于了解内容) 2.1.完整的建表语句 2.2.什么是存储引擎呢 ...
- Vue+SpringBoot+ElementUI实战学生管理系统-9.教师管理模块
1.章节介绍 前一篇介绍了班级管理模块,这一篇编写教师管理模块,需要的朋友可以拿去自己定制.:) 2.获取源码 源码是捐赠方式获取,详细请QQ联系我 :)! 3.实现效果 教师列表 修改教师 4.模块 ...
- QT - Day 5
1 event事件 用途:用于事件的分发 也可以做拦截操作,不建议 bool event( QEvent * e); 返回值 如果是true 代表用户处理这个事件,不向下分发了 e->ty ...
- TerminateJobObject是使用
注意: AssignProcessToJobObject仅适用于win32 desktop app, 比如notepad是适用的,calculator是不适用的 下面的demo是将notepad的句柄 ...