C++_虚函数表

虚函数表

  • 介绍
  • 源码
  • 运行结果
    • 笔记扩充
    • 函数名联编
    • 静态联编
    • 动态联编

介绍

1.编译器通过指针或引用调用虚函数,不会立即生成函数调用指令,而是用 二级函数指针 代替

1.1确定真实类型
1.2找到虚函数表从而找到入口地址
1.3根据入口地址调用函数(PS:俗称 函数指针)

** 虚函数表 内部存储格式展示**
在这里插入图片描述
代码图详解
在这里插入图片描述

源码

#include<iostream>
#include<string>
using namespace std;class N
{
public:void foo(){cout << "N::foo" << endl;}void ber(){cout << "N::ber" << endl;}int m_a;int m_b;
};
class A
{
public:virtual void foo(){cout << "A::foo" << endl;}virtual void ber(){cout << "A::ber" << endl;}double m_a;int m_b;
};
class B:public A
{
public:void foo(){cout << "B::foo" << endl;}void ber(){cout << "B::ber" << endl;}
};
void main()
{A a;//在A类型中会多生成一个最大类型的 字节指针(内部存储的是函数指针)B b;//在B类型中会多生成一个最大类型的 字节指针(内部存储的是函数指针)A*pa = &b;//获取 N 类型大小cout << "sizeof(N):" << sizeof(N);//获取 N 相对于 m_a 的距离cout << ",m_a:" << offsetof(N, m_a);//获取 N 相对于 m_b 的距离cout << ",m_b:" << offsetof(N, m_b) << endl;//获取 A 类型大小cout << "sizeof(N):" << sizeof(A);//获取 A 相对于 m_a 的距离cout << ",m_a:" << offsetof(A, m_a);//获取 A 相对于 m_b 的距离cout << ",m_b:" << offsetof(A, m_b) << endl;cout << "-------------------分隔符-------------------" << endl;
//函数指针调用函数void *vf_ptr = *(void**)&a; //获取 A类型 中 a 地址cout <<"A类型 中 a 地址为:"<< vf_ptr << endl;    //打印 A类型 中 a 地址cout << "-------------------分隔符-------------------" << endl;//取别名typedef void(*VFUN)(void*); //VFUN 相当于 void(*)  // *VFUN 函数指针类型typedef VFUN* VPTR;         //VPTR 相当于 void(**) //指向函数指针类型的指针 VPTR虚函数表类型VPTR _vfptr = *(VPTR*)&a;   //获取 A类型 中 a 地址cout << "A类型 中 a 地址为:" << _vfptr << endl;   //打印 A类型 中 a 地址a.foo();                    //正常调用方法_vfptr[0](&a);              //编译器中调用方法 结果为:A::foo_vfptr[1](&a);              //编译器中调用方法 结果为:A::bercout << "-------------------分隔符-------------------" << endl;VPTR _vfptr1 = *(VPTR*)&b;   //获取 B类型 中 b 地址cout << "B类型 中 b 地址为:" << _vfptr1 << endl;   //打印 B类型 中 b 地址b.foo();                    //正常调用方法_vfptr1[0](&b);              //编译器中调用方法 结果为:B::foo_vfptr1[1](&b);              //编译器中调用方法 结果为:B::bersystem("pause");
}

运行结果

sizeof(N):8,m_a:0,m_b:4
sizeof(N):24,m_a:8,m_b:16
-------------------分隔符-------------------
A类型 中 a 地址为:0015DC80
-------------------分隔符-------------------
A类型 中 a 地址为:0015DC80
A::foo
A::foo
A::ber
-------------------分隔符-------------------
B类型 中 b 地址为:0015DCA0
B::foo
B::foo
B::ber
请按任意键继续. . .

笔记扩充

void(VFUN)(void) 的代码解释,参考下列源码

#include<iostream>
#include<string>
using namespace std;//构造3个通用函数
void TEST1(void) { printf("test1\n"); }//函数定义  
void TEST2(void) { printf("test2\n"); }//函数定义  
void TEST3(void) { printf("test3\n"); }//函数定义  
//声明(取别名为:(*func)函数)是 void 类型
typedef void(*func)(void);void test(int i)
{func vTask[3] = { &TEST1, &TEST2, &TEST3 };//func 类型代替 void功能func fun = vTask[i];//将vTask函数赋值到fun中(*fun)();//调用(*fun) ()函数
}
void main()
{test(0);test(1);test(2);system("pause");
}

函数名联编

将源代码中的函数调用解释为执行特定的函数代码块被称为 函数名联编

静态联编

1.静态联编是指 联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的, 又称为:早期联编
2.静态联编对函数的选择是基于指向对象的指针或者引用的类型
3.优点是效率高,灵活性差
4.静态联编是根据 所定义的类 来调用 类中函数(PS:相当于直接调用当前类代码,不会做任何检查)

动态联编

1.动态联编 是指联编在程序运行时动态地进行,这种联编又称为晚期联编。或动态束定
2.动态联编对成员函数的选择是基于对象的类型
3.优点是灵活性强,效率低
4.实际上是在运行时虚函数(virtual)的实现。(PS:先进行检查后,根据当时的情况来确定调用哪个同名函数)
动态联编 调用步骤:

1.1确定真实类型
1.2找到虚函数表从而找到入口地址
1.3根据入口地址调用函数(PS:俗称 函数指针)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/342919.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谈谈Spring Bean

一、IoC 容器 IoC 容器是 Spring 的核心&#xff0c;Spring 通过 IoC 容器来管理对象的实例化和初始化&#xff08;这些对象就是 Spring Bean&#xff09;&#xff0c;以及对象从创建到销毁的整个生命周期。也就是管理对象和依赖&#xff0c;以及依赖的注入等等。 Spring 提供…

【QT实战】使用QT5制作一个简易串口助手详细教程,支持打包转发

文章目录 制作串口助手工程所涉及到的头文件ui布局制作串口配置选项添加修饰制作串口打印信息界面添加背景图片 函数查找串口并且添加到ui界面上显示串口数据接收槽函数串口发送槽函数打开串口槽函数 将串口助手封装成可执行文件 本项目的开发环境&#xff1a;windowsQT5qtcrea…

【Docker】数据管理

&#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Docker的相关操作吧 目录 &#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 前言 一.数据卷 示例演示 示例剖析…

C#核心--实践小项目(贪吃蛇)

C#核心实践小项目 -- 贪吃蛇 必备知识点--多脚本文件 &#xff08;可观看CSharp核心--52集进行了解&#xff09; 必备知识点--UML类图 必备知识点--七大原则 贪吃蛇 项目展示 控制方向的是&#xff1a;WSAD 确定键是&#xff1a;J 需求分析&#xff08;UML类图&#xff09…

《豫鄂烽火燎原大小焕岭》:一部穿越时空的历史史诗

《豫鄂烽火燎原大小焕岭》&#xff1a;一部穿越时空的历史史诗 一部赓续红色血脉的生动教材 一部讴歌时代英雄和人民精神宝典 当历史的烽烟渐渐远去&#xff0c;留下的是一页页泛黄的记忆和无数英雄的壮丽诗篇。李传铭的力作《豫鄂烽火燎原大小焕岭》正是这样一部深情的回望&am…

小智ToDo:日程待办清单管理的智能助手

在繁忙的工作与生活中&#xff0c;有效的时间管理和任务规划是提高效率的关键。今天&#xff0c;我们来探讨一款名为“小智ToDo”的日程待办清单管理工具&#xff0c;它以其多端数据同步、备忘提醒、日程管理等实用功能&#xff0c;为用户提供了便捷的时间管理解决方案。 小智T…

Jmerer之FTP测试

1、文件上传下载测试&#xff0c;可以使用sample:FTP请求&#xff0c;当然也可以使用HTTP Request采样器中的File Upload向服务器上传文件 2、本章重点介绍FTP请求进行文件的上传下载测试&#xff0c;添加 FTP请求&#xff0c;界面主要配置如下&#xff1a; Server Name or I…

认识异常及异常处理机制之try-catch

异常类 什么是异常&#xff1f;就像人会犯错一样&#xff0c;程序在运行的过程中也会犯错。程序中的错误有两类&#xff0c;一类称为Error&#xff08;错误&#xff09;&#xff0c;另一类称为Exception&#xff08;异常&#xff09;。Error类和Exception类都为Throwable的子类…

基于matlab实现AUTOSAR软件开发---答疑5

最近还是经常有人反馈mode switch的枚举搞不定,我在统一回复下,希望可以解决大家的疑问 运行这个脚本即可,运行成功后,就已经存在于SIMULINK系统里了,程序中可以直接识别到的, 但是运行之后 在matlab基础工作区里也是看不到枚举的,这点大家要注意,不要纠结这个,实际…

STM8入门|第一个工程

开发软件 不支持Keil&#xff0c;使用IAR for STM8&#xff0c;注意 IAR系列有很多种 STM8对应软件是 IAR for STM8 软件下载&#xff1a; 官网下载地址&#xff0c;官网版本下载比较麻烦&#xff0c;可以按教程网盘地址下载。 下载安装教程&#xff1a; https://www.cnblogs…

flutter使用getx进行数据状态管理,实现页面响应式

无论是什么样的应用&#xff0c;都还是需要最基础的数据来支撑的&#xff0c;而且不同的页面之间可能需要共享数据状态&#xff0c;这就显得数据状态管理非常有必要了。因为我这里使用了get依赖库&#xff0c;所以就可以直接在项目中使用getx来管理状态&#xff0c;不想再使用别…

适用于动态 IT 环境的服务器流量监控软件

服务器在网络性能中起着至关重要的作用&#xff0c;这意味着保持其最佳容量至关重要。企业需要将 AI、ML 和云技术融入其 IT 中&#xff0c;从而提供充分的敏捷性、安全性和灵活性&#xff0c;在这方面&#xff0c;服务器流量监控已成为当务之急。通过定期监控通信、跟踪流量上…