【C++】多态 ⑦ ( 多态机制实现原理 | 虚函数表概念 | 虚函数表工作机制 | vptr 指针 | 虚函数表运行时机制 | 虚函数与动态联编 )

文章目录

  • 一、多态原理
    • 1、多态成立的三个条件
    • 2、虚函数表概念
    • 3、虚函数表工作机制
    • 4、vptr 指针
    • 5、虚函数表运行时机制
    • 6、虚函数与动态联编
  • 二、代码示例 - 虚函数表
    • 1、代码实例分析 - 虚函数表创建与使用
    • 2、完整代码示例





一、多态原理




1、多态成立的三个条件


" 多态 " 实现需要满足以下三个条件 :

  • 首先 , 要有 继承关系 ;
  • 然后 , 父类中的函数需要有 virtual 关键字修饰 , 子类重写该 " 虚函数 " ;
  • 最后 , 父类指针 或 父类引用 指向 子类的对象 ;

满足 ① 继承 , ② 虚函数重写 , ③ 父类指针/引用指向子类对象 三个条件 , 即可实现多态 ;


2、虚函数表概念


" 多态 " 的机制 , 由 " 虚函数表 " 实现 ;

" 虚函数表 " , 英文名称为 " Virtual Function Table " , 简称 Vtable ;


C++ 编译器 通过将 虚函数指针 放入 基类 的 虚函数表中 , 实现在 运行时 根据实际对象的类型 来调用对应的 virtual 虚函数 ;


虚函数表 是由 C++ 编译器 自动维护的 , 对 程序员 透明 ;


3、虚函数表工作机制


" 虚函数表 " 由 C++ 编译器 负责 创建 与 维护 , 被 virtual 关键字 修饰的 虚函数 , 会自动 被 C++ 编译器 存储到 " 虚函数表 " 中 ;


虚函数表 创建 : 在 类 中使用 virtual 关键字 声明 虚函数 时 , C++ 编译器 会自动为该类生成 " 虚函数表 " ;

  • 生成虚函数表的前提是 至少有 1 个虚函数 ;
  • 如果 没有虚函数 , 就不会生成虚函数表 ;
  • 如果 类 中有 virtual 虚函数 , 则 该类的 每个对象 中 , 都有一个 指向 虚函数表的 vptr 指针 ;

虚函数表 存储 虚函数指针 : " 虚函数表 " 是 存储 " 类成员函数指针 " 的 数据结构 , 是一个 函数指针数组 , 数组中的元素都是函数指针 , 具体存储的都是 指向 类中的虚函数 的指针 ;

  • 如果 子类 中 , 重写了 父类的 virtual 虚函数 , 那么 C++ 编译器会在 子类 虚函数表 中放入该 子类虚函数的 函数指针 ;

4、vptr 指针


如果 C++ 类中存在 virtual 虚函数 , 在创建对象时 , 会生成 虚函数表 Virtual Function Table , 简称 vtable ;

C++ 编译器 编译 代码时 , 会自动为该类 添加 一个 vptr 指针 成员变量 , 该指针 会指向 虚函数表 ;


5、虚函数表运行时机制


" 虚函数表 " 在 C++ 编译器 编译 时 生成 , 运行时 存储在可执行文件的内存中 ;

程序运行时 , 根据对象的类型信息 , 可以通过 虚函数表 来动态地调用对应的函数 ;


虚函数表 与 对象 是一一对应的 , 如果 父类指针 指向 的对象 , 调用 虚函数 , 则会去 对象对应的 虚函数表 中查找函数 , 找到的肯定是 对象的 虚函数 ;

虚函数表机制 可以避免在运行时进行类型判断 , 提高了程序的效率和可维护性 ;


6、虚函数与动态联编


C++ 编译器 确定 函数 是否为 virtual 虚函数 ;

  • 非虚函数的静态联编 : 如果 函数 没有被 virtual 关键字修饰 , 该函数 不是 虚函数 , 该函数 可以被确定为 普通 成员函数 , 则使用 " 静态联编 " , 在编译时 就可以确定 是否调用该函数 ;
  • 虚函数的动态联编 : 如果 函数 被 virtual 关键字修饰 , 则该函数是 虚函数 , C++ 编译器编译该类时 , 会自动生成一个 虚函数表 , 并为对象设置一个 vptr 指针 , 指向该 虚函数表 , 在调用时 , 需要查找 vptr 指向的 虚函数表 中的 虚函数 , 查找个调用 虚函数 的操作是在运行时进行的 , 这是 " 动态联编 " ;




二、代码示例 - 虚函数表




1、代码实例分析 - 虚函数表创建与使用


在下面的代码中 , Parent 是父类 , 其中定义了 virtual 虚函数 , Child 子类中重写了该 虚函数 ;

使用 如下代码 , 创建 Child 子类对象时 , 发现有 virtual 虚函数 会创建 虚函数表 , 在对象中会自动添加 vptr 指针 成员变量 指向 虚函数表 首地址 ;

	// 创建 Child 子类对象时// 发现有 virtual 虚函数 会创建 虚函数表// 在对象中使用 vptr 指针指向 虚函数表 首地址Child c;

虚函数表 中 存储了 多个 虚函数 的 入口地址 ;


将 Child 对象的地址 赋值给 Parent * 指针时 , 通过 Parent* 指针调用 fun 虚函数 ,

C++ 编译器 根本不会去关心 当前调用的函数 是 Parent 父类的 还是 Child 子类的 ,

而是根据对象中的 vptr 指针 指向的 虚函数表 调用 对应的 虚函数 ;


父类对象 和 子类对象 中 都有一个 vptr 指针 成员变量 ,

当调用 虚函数 时 , 会根据对象中的 vptr 指针查找 虚函数表 中的 对应 虚函数 ;


2、完整代码示例


代码示例 :

#include "iostream"
using namespace std;// 父类
class Parent {
public:virtual void fun(int a){cout << "执行 父类 virtual void fun(int a) 函数" << endl;}
};// 子类
class Child : public Parent {
public:virtual void fun(int a){cout << "执行 子类 virtual void fun(int a) 函数" << endl;}
};int main() {Parent* p;// 创建 Child 子类对象时// 发现有 virtual 虚函数 会创建 虚函数表// 在对象中使用 vptr 指针指向 虚函数表 首地址Child c;// 将父类指针指向子类对象p = &c;// 通过父类指针调用子类对象的 fun 函数p->fun(1);// 控制台暂停 , 按任意键继续向后执行system("pause");return 0;
}

执行结果 :

执行 子类 virtual void fun(int a) 函数
Press any key to continue . . .

在这里插入图片描述

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

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

相关文章

网络爬虫——urllib(5)

前言&#x1f36d; ❤️❤️❤️网络爬虫专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Python网络爬虫_热爱编程的林兮的博客-CSDN博客 上一篇我们讲解有关ajax的相关案例&#xff0c;下面我们来学习新的关于ur…

CN考研真题知识点二轮归纳(3)

持续更新&#xff0c;上期目录&#xff1a; CN考研真题知识点二轮归纳&#xff08;2&#xff09;https://blog.csdn.net/jsl123x/article/details/134111760?spm1001.2014.3001.5501 1.TCP/IP 名称&#xff1a;传输控制协议/网络协议&#xff0c;是一个协议族&#xff0c;主…

[已解决]虚拟机之前能正常上网,重启之后无法连接网络问题的解决方法

虚拟机之前网络正常&#xff0c;重启之后却始终连接不上网络。 找了许多方法&#xff0c;终于发现一种便捷有效的方法。 解决方法如下&#xff1a; 1、将网络模式更改为NAT模式., 2、打开终端窗口&#xff0c;输入如下命令。 sudo service network-manager stopsudo rm /var/l…

【Kubernetes 基本概念】Kubernetes 的架构和核心概念

目录 一、Kurbernetes1.1 简介1.2 为什么要用K8s?1.3 K8s的特性 二、Kurbernetes集群架构与组件三、Kurbernetes的核心组件3.1 Master组件3.1.1 Kube-apiserver3.1.2 Kube-controller-manager3.1.3 Kube-scheduler 3.2 配置存储中心——etcd3.3 Node组件3.3.1 Kubelet3.3.2 Ku…

Win10系统 如何使用cmd脚本命令,连接到指定WIFI并免手工输入WIFI密码连接?

环境&#xff1a; Win10 专业版 19041 WiFi 名称&#xff1a;LTG 问题描述&#xff1a; Win10系统 如何使用cmd脚本命令&#xff0c;连接到指定WIFI并免手工输入WIFI密码连接&#xff1f; 解决方案&#xff1a; 1.找一台已经连接过LTG这个wifi的电脑&#xff0c;导出.xlm配…

diffusers-Understanding models and schedulers

https://huggingface.co/docs/diffusers/using-diffusers/write_own_pipelinehttps://huggingface.co/docs/diffusers/using-diffusers/write_own_pipelinediffusers有3个模块&#xff1a;diffusion pipelines&#xff0c;noise schedulers&#xff0c;model。这个库很不错&…

神经网络的解释方法之CAM、Grad-CAM、Grad-CAM++、LayerCAM

原理优点缺点GAP将多维特征映射降维为一个固定长度的特征向量①减少了模型的参数量&#xff1b;②保留更多的空间位置信息&#xff1b;③可并行计算&#xff0c;计算效率高&#xff1b;④具有一定程度的不变性①可能导致信息的损失&#xff1b;②忽略不同尺度的空间信息CAM利用…

如何选择最适合的技术栈来进行外卖App系统开发?

选择合适的技术栈对于外卖App系统的开发至关重要。以下是针对不同方面的考量&#xff1a; 1. 后端开发 对于后端开发&#xff0c;选择一个稳定、高效的框架是关键。Node.js、Python&#xff08;Django或Flask&#xff09;、Ruby on Rails等都是流行的选择。举例&#xff0c;…

Mysql5.7安装配置详细图文教程(msi版本)

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

【学习SonarQube记录】如何在windows上安装SonarQube及安装中文语言包

学习SonarQube记录 第一章 如何在windows上安装SonarQube及安装中文语言包 文章目录 学习SonarQube记录前言一、SonarQube是什么&#xff1f;二、安装步骤1.准备工作2.安装SonarQube 总结 前言 公司近期有代码完整性检测的需求&#xff0c;于是来学习相关工具SonarQube 一、S…

GTS GtsUnofficialApisUsageTestCases Failed

GTS 测试GtsUnofficialApisUsageTestCases失败如下&#xff1a; junit.framework.AssertionFailedError: There are 102 violation(s) com.google.android.gm / Landroid/window/BackEvent;->getProgress()F / BLOCKED / LINKING com.google.android.gm / Landroid/window/…

一文彻底理解python浅拷贝和深拷贝

目录 一、必备知识二、基本概念三、列表&#xff0c;元组&#xff0c;集合&#xff0c;字符串&#xff0c;字典浅拷贝3.1 列表3.2 元组3.3 集合3.4 字符串3.5 字典3.6 特别注意可视化展示浅拷贝总结 四、列表&#xff0c;元组&#xff0c;集合&#xff0c;字符串&#xff0c;字…