当前位置: 首页 > news >正文

C++多态原理

请看下面的程序,该程序演示了多态类对象存储空间的大小。

#include <iostream>
using namespace std;
class A
{public:int i;virtual void func() {}virtual void func2() {}
};
class B : public A
{int j;void func() {}
};
int main()
{cout << sizeof(A) << ", " << sizeof(B); //输出 8,12return 0;
}

在 32 位编译模式下,程序的运行结果是:
8, 12
如果将程序中的 virtual 关键字去掉,输出结果变为:
4, 8
  对比发现,有了虚函数以后,对象所占用的存储空间比没有虚函数时多了 4 个字节。实际上,任何有虚函数的类及其派生类的对象都包含这多出来的 4 个字节,这 4 个字节就是实现多态的关键——它位于对象存储空间的最前端,其中存放的是虚函数表的地址。
  每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着该虚函数表的指针(可以认为这是由编译器自动添加到构造函数中的指令完成的)。
  虚函数表是编译器生成的,程序运行时被载入内存。一个类的虚函数表中列出了该类的全部虚函数地址。例如,在上面的程序中。
类 A 对象的存储空间以及虚函数表(假定类 A 还有其他虚函数)如下图所示。
在这里插入图片描述
类 B 对象的存储空间以及虚函数表(假定类 B 还有其他虚函数)如下图所示。
在这里插入图片描述
  多态的函数调用语句被编译成根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的一系列指令。

假设 pa 的类型是 A*,则 pa->func() 这条语句的执行过程如下:

  1. 取出 pa 指针所指位置的前 4 个字节,即对象所属的类的虚函数表的地址(在 64 位编译模式下,由于指针占 8 个字节,所以要取出 8 个字节)。如果 pa 指向的是类 A 的对象,则这个地址就是类 A 的虚函数表的地址;如果 pa 指向的是类 B 的对象,则这个地址就是类 B 的虚函数表的地址。

  2. 根据虚函数表的地址找到虚函数表,在其中查找要调用的虚函数的地址。不妨认为虚函数表是以函数名作为索引来查找的,虽然还有更高效的查找方法。如果 pa 指向的是类 A 的对象,自然就会在类 A 的虚函数表中查出 A::func 的地址;如果 pa 指向的是类 B的对象,就会在类 B 的虚函数表中查出 B::func 的地址。类 B 没有自己的 func2 函数,因此在类 B 的虚函数表中保存的是 A::func2 的地址,这样,即便 pa 指向类B 的对象,pa->func2();这条语句在执行过程中也能在类 B 的虚函数表中找到 A::func2 的地址。

  3. 根据找到的虚函数的地址调用虚函数。

  由以上过程可以看出,只要是通过基类指针或基类引用调用虚函数的语句,就一定是多态的,也一定会执行上面的查表过程,哪怕这个虚函数仅在基类中有,在派生类中没有。
  多态机制能够提高程序的开发效率,但是也增加了程序运行时的开销。虚函数表、各个对象中包含的 4 个字节的虚函数表的地址都是空间上的额外开销;而查虚函数表的过程则是时间上的额外开销。
  在计算机发展的早期,计算机非常昂贵稀有,运行速度慢,计算机的运算时间和内存是宝贵的,因此人们不惜多花人力编写运行速度更快、更节省内存的程序;如今,计算机的运算时间和内存往往没有人的时间宝贵,运算速度也很快,因此,在用户可以接受的前提下,降低程序运行的效率以提升人员的开发效率就是值得的了。“多态”的应用就是典型例子。

http://www.lryc.cn/news/24293.html

相关文章:

  • PMP认证与NPDP认证哪个含金量高?
  • 改进YOLOv7-Tiny系列:首发改进结合BiFPN结构的特征融合网络,网络融合更多有效特征,高效涨点
  • PPC Insights系列:洞见安全多方图联邦
  • SQLite注入记录(目前最全、核心函数用法、布尔盲注、时间盲注、webshell、动态库,绕过方式)
  • Java简单的生成/解析二维码(zxing qrcode)
  • 若依项目导出后端响应的Excel文件流处理
  • 华为OD机试【独家】提供C语言题解 - 数组排序
  • JVM详解——内存结构
  • Jvisualvm监控Tomcat以及相关参数优化
  • 界面组件DevExpress WinForms v22.2 - 全面升级数据展示功能
  • 正点原子第一期
  • 「mysql是怎样运行的」第24章 一条记录的多幅面孔---事务的隔离级别与MVCC
  • 入门Java第十五天 线程
  • 探索用卷积神经网络实现MNIST数据集分类
  • MySQL 索引失效场景
  • Xcode开发工具,图片放入ios工程
  • 操作系统权限提升(十九)之Linux提权-SUID提权
  • 直播 | StarRocks 实战系列第三期--StarRocks 运维的那些事
  • KingabseES执行计划-分区剪枝(partition pruning)
  • Operator-sdk 在 KaiwuDB 容器云中的使用
  • 【数据挖掘】2、数据预处理
  • (四十六)大白话在数据库里,哪些操作会导致在表级别加锁呢?
  • 【Android源码面试宝典】MMKV从使用到原理分析(二)
  • 如何使用ADFSRelay分析和研究针对ADFS的NTLM中继攻击
  • 【Python学习笔记】第二十二节 Python XML 解析
  • 5分钟轻松拿下Java枚举
  • 华为OD机试【独家】提供C语言题解 - 最小传递延迟
  • 【Web前端】关于JS数组方法的一些理解
  • 多智能体集群协同控制笔记(1):线性无领航多智能体系统的一致性
  • hadoop-Yarn资源调度器【尚硅谷】