虚函数表

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++多态底层原理:虚函数表的更多相关文章

  1. C++ 虚函数表与多态 —— 多重继承的虚函数表 & 内存布局

    多重继承的虚函数表会有两个虚表指针,分别指向两个虚函数表,如下代码中的 vptr_s_1.vptr_s_2,Son类继承自 Father 和 Mather 类,并且改写了 Father::func_1 ...

  2. C++ 虚函数表与多态 —— 继承的虚函数表 & 内存布局

    1. 使用继承的虚函数表: 如果不涉及多重继承,每个类只有1个虚函数表,当子类继承父类后,子类可以自己改写和新增虚函数,如下图所示: 子类重写 func_1 后,子函数的 func_1 将会有新的逻辑 ...

  3. 虚函数表-C++多态的实现原理

    目录 1.说明 2.虚函数表 3.代码示例 参考:http://c.biancheng.net/view/267.html 1.说明 我们都知道多态指的是父类的指针在运行中指向子类,那么它的实现原理是 ...

  4. C++中的虚函数以及虚函数表

    一.虚函数的定义 被virtual关键字修饰的成员函数,目的是为了实现多态 ps: 关于多态[接口和实现分离,父类指针指向子类的实例,然后通过父类指针调用子类的成员函数,这样可以让父类指针拥有多种形态 ...

  5. 深入剖析C++多态、VPTR指针、虚函数表

    在讲多态之前,我们先来说说关于多态的一个基石------类型兼容性原则. 一.背景知识 1.类型兼容性原则 类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代.通过公有继承,派 ...

  6. C++ 虚函数表与多态 —— 虚函数表的内存布局

       C++面试经常会被问的问题就是多态原理.如果对C++面向对象本质理解不是特别好,问到这里就会崩. 下面从基本到原理,详细说说多态的实现:虚函数 & 虚函数表.   1. 多态的本质: 形 ...

  7. 从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数

    一.多态 多态性是面向对象程序设计的重要特征之一. 多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为. 多态的实现: 函数重载 运算符重载 模板 虚函数 (1).静态绑定与动态绑 ...

  8. C++虚函数表原理

    C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指 向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有“多种形态”,这是一种泛型技 ...

  9. 类虚函数表原理实现分析(当我们将虚表地址[n]中的函数替换,那么虚函数的实现就由我们来控制了)

    原理分析 当调用一个虚函数时, 编译器生成的代码会调用 虚表地址[0](param1, param2)这样的函数. 已经不是在调用函数名了. 当我们将虚表地址[n]中的函数实现改为另外的函数, 虚函数 ...

  10. C++多态,虚函数,虚函数表,纯虚函数

    1.多态性   指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作. C++支持两种多态性:编译时多态性,运行时多态性.    a.编译时多态性:通过重载函数实现 ,模板(2次编译)  ...

随机推荐

  1. ESP8266的AT指令模块程序

    最新代码可点击下载:ESP8266 模块代码 和以下代码实现方式不一致,更加自由可控 本段代码只是测试了esp8266作为服务器端使用,没有测试作为客户端使用. 没有超长延时等待或死循环等待AT指令反 ...

  2. Linux--split命令(将一个大文件拆分成多个小文件)

    一.split命令简介 split是Linux系统中的一个文件拆分命令,它可以将一个大文件拆分成多个小文件.这对于处理大型文件,或者需要将数据分解到多个文件中的场景非常有用. 二.split命令的使用 ...

  3. NC50615 取石子游戏 2

    题目链接 题目 题目描述 有一种有趣的游戏,玩法如下: 玩家:2人: 道具:N堆石子,每堆石子的数量分别为 \(X_1,X_2,...,X_n\) ​: 规则: ​ 游戏双方轮流取石子: ​ 每人每次 ...

  4. Python 中Time 模块

    python的time内置模块是一个与时间相关的内置模块,很多人喜欢用time.time()获取当前时间的时间戳,利用程序前后两个时间戳的差值计算程序的运行时间,如下: 1.使用time.time() ...

  5. Oracle数据库报ORA-01078和LRM-00109错误解决方法

    创建实例后,进入sqlplus启动报错:     sqlplus / as sysdba;     SQL*Plus: Release 11.1.0.6.0 - Production on Wed A ...

  6. Python之初级RPG小游戏

    在国外网站上找到一个练习Python的小游戏感觉不错,自己实现了一下. 通过该练习你能学到: 元组 字典 简单定义函数和封装 条件控制语句 游戏说明 以下是3个房间和1个花园: Hall 客厅 有一把 ...

  7. Java Base64编码使用介绍

    Base64编码介绍     BASE64 编码是一种常用的字符编码,Base64编码本质上是一种将二进制数据转成文本数据的方案. 但base64不是安全领域下的加密解密算法.能起到安全作用的效果很差 ...

  8. MyBatis实现多行合并(collection标签使用)

    举个栗子 现有如下表结构,用户表.角色表.用户角色关联表. 一个用户有多个角色,一个角色有可以给多个用户,也即常见的多对多场景. 现有这样一个需求,分页查询用户数据,除了用户ID和用户名称字段,还要查 ...

  9. win32-SetupDiSetClassInstallParamsW的使用

    SetupDiSetClassInstallParams函数一般是用来禁用/启用某个设备 比如我们可以禁用网络适配器 /* for Devpkey */ #define INITGUID /* dep ...

  10. RK3588开发笔记(一):基于方案商提供的宿主机交叉编译Qt5.12.10

    前言   rk3588开发车机,方案上提供的宿主机只是编译rk sdk的版本,并未编译好Qt,那么需要自行交叉编译Qt系统.选择的Qt的版本为5.12.10.   宿主机准备   下载并打开宿主机,只 ...