虚函数表

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. Load-balanced-online-OJ-system 负载均衡的OJ系统项目

    前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总 本项目Github地址 - ...

  2. 来了!HelloGitHub 年度热门开源项目

    年关将至,「HelloGitHub 月刊」也迎来了年终盘点时刻. 在过去的一年里,「HelloGitHub 月刊」一共分享了 520 个开源项目.我始终秉持着分享 GitHub 上有趣.入门级开源项目 ...

  3. 【开工大吉】推荐4款开源、美观的WPF UI组件库

    前言 经常有小伙伴在技术群里提问:WPF有什么好用的UI组件库?,今天大姚给大家推荐4款开源.美观的WPF UI组件库. WPF介绍 WPF 是一个强大的桌面应用程序框架,用于构建具有丰富用户界面的 ...

  4. js 实现call和apply方法,超详细思路分析

    壹 ❀ 引 我在 五种绑定策略彻底弄懂this 一文中,我们提到call,apply,bind属于显示绑定,这三个方法都能直接修改this指向.其中call与apply比较特殊,它们在修改this的同 ...

  5. Elasticsearch Web管理工具

    Cerebro是一个开源的elasticsearch web管理工具 首先,下载Elasticsearch https://www.elastic.co/guide/en/elasticsearch/ ...

  6. Android里使用AspectJ实现双击自定义注解

    创建注解 首先创建一个双击注解. import java.lang.annotation.ElementType; import java.lang.annotation.Retention; imp ...

  7. python 创建动态类

    一般情况下多数是预先定义类 而少数特殊情况就需要去动态创建类了,直接贴代码. class BaseModel(Model): class Meta: database = _tb class_new ...

  8. 搜索引擎RAG召回效果评测MTEB介绍与使用入门

    RAG 评测数据集建设尚处于初期阶段,缺乏针对特定领域和场景的专业数据集.市面上常见的 MS-Marco 和 BEIR 数据集覆盖范围有限,且在实际使用场景中效果可能与评测表现不符.目前最权威的检索榜 ...

  9. 公司官网建站笔记(二):在云服务器部署PHP服务(公网访问首页)

    前言   上一篇重新安装了CentOS8.2之后,接下来开始安装部署PHP服务器,让公网可以访问到我们部署的PHP服务器首页.   背景   为什么自行搭建,是因为红胖子专业做相关Qt软件以及终端设备 ...

  10. 第131篇:如何上传一个npm包

    好家伙, NPM的全称是Node Package Manager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准.  NPM是世界上最大的软件注册表. 1.首先我们 ...