C++多态之虚函数表详解及代码示例

news2024/12/19 22:58:29

引言

C++相对其他面向对象语言来说,之所以灵活、高效。很大程度的占比在于其多态技术和模板技术。C++虚函数表是支撑C++多态的重要技术,它是C++动态绑定技术的核心。

如果对多态还不了解的小伙伴,可以点这里C++多态详解基础篇。

在不考虑继承的情况下,如果一个类中有虚函数,那么这个类就有一个虚函数表,这个虚函数表在编译期间确定,这个类对象共享。而这个类所有的实例化对象中都有一个虚函数指针,这个虚函数指针就指向同一份虚函数表。

一、多态的使用及内存分布图

假设现在有个基类的class A,A中有虚函数,class B继承了A,并且B重写了A中的虚函数。那么,我们在使用多态的时候,通常会有两种方式:

// 方式一
class A* a = new class B;
// 方式二
class B b;
class A *a = &b;

上面两种方式,都是用父类的指针指向了子类的对象。区别在于,方式一的子类对象是new出来的,存在于堆区;方式二的子类对象则保存在栈区。
在这里插入图片描述

二、多重继承的虚函数表

2.1 代码示例

class A {
public:

    void func1(){ std::cout << "Class A func1" << std::endl; }
    void func2(){ std::cout << "Class A func2" << std::endl; }

    virtual void v_func1(){ std::cout << "Class A v_func1" << std::endl; }
    virtual void v_func2(){ std::cout << "Class A v_func2" << std::endl; }

private:
    int m_aMember;
};


class B : public A{
public:

    void func1(){ std::cout << "Class B func1" << std::endl; }

    virtual void v_func1(){ std::cout << "Class B v_func1" << std::endl; }

private:
    int m_bMember;   
};

class C : public B{
public:

    void func2(){ std::cout << "Class C func2" << std::endl; }

    virtual void v_func2(){ std::cout << "Class C v_func2" << std::endl; }

private:
    int m_bMember;   
};

int main(int argc, char* argv[])
{
	std::cout << "============== class B : public A ============" << std::endl;
    // class B  b;
    // class A *a = &b;
    class A* a = new class B;
    a->func1();
    a->func2();
    a->v_func1();
    a->v_func2();

    return 0;
}

上面的代码中:

  • 父类A中,有属性m_aMember,普通函数func1() func2(),有虚函数 v_func1() v_func2()
  • class B继承了A,有属性m_bMember,普通函数func1(),虚函数 v_func1()
  • class C继承了B,有属性m_cMember,普通函数func2(),虚函数 v_func2()

注意,父类的虚函数表和子类的虚函数表不是同一张表,虚函数表是在编译时确定的,虚函数保存在代码段,仅有一份,属于类而不属于某个具体的实例对象

2.2 多重继承图解

在这里插入图片描述

B继承于A,B的虚函数表示在A的虚函数表的基础上有所改动。改的部分就是子类B重写的父类A的虚函数v_func1()。假设此时B没有重写A任何一个虚函数,那么B类的虚函数表和类A的虚函数表的内容是相同的。

运行结果分析:
在这里插入图片描述

毫无疑问,对于A类型的指针来说,可见的部分只有图中红色框的部分。因为 B重写了A的虚函数v_func1(),所以在B的虚函数表会覆盖父类A的虚函数v_func1()。所以,会调用到B的虚函数v_func1(),从而实现多态。

同理,不难猜出C的逻辑结构图:C继承于B,B继承于A

在这里插入图片描述

int main(int argc, char* argv[])
{
    std::cout << "C -> B ->  A  " << std::endl;
    std::cout << "======= class A* a = new class C =======" << std::endl;
    class A* ac = new class C;
    ac->func1();
    ac->func2();
    ac->v_func1();
    ac->v_func2();

    std::cout << "======= class B* b = new class C =======" << std::endl;
    class B* bc = new class C;
    bc->func1();
    bc->func2();
    bc->v_func1();
    bc->v_func2();

    return 0;
}

运行结果:
在这里插入图片描述

三、多继承的虚函数表

多继承指的是一个类同时继承多个基类,如果每个基类都有虚函数,那么对应的每个基类都有自己的虚函数表。

class A {
public:

    void func1(){ std::cout << "Class A func1" << std::endl; }
    void func2(){ std::cout << "Class A func2" << std::endl; }

    virtual void v_func1(){ std::cout << "Class A v_func1" << std::endl; }
    virtual void v_func2(){ std::cout << "Class A v_func2" << std::endl; }

private:
    int m_aMember;
};


class B {
public:

    void func1(){ std::cout << "Class B func1" << std::endl; }

    virtual void v_func1(){ std::cout << "Class B v_func1" << std::endl; }
    virtual void v_func2(){ std::cout << "Class B v_func2" << std::endl; }
    virtual void v_func4(){ std::cout << "Class B v_func4" << std::endl; }

private:
    int m_bMember;   
};

class C : public A, public B{
public:

    void func1(){ std::cout << "Class C func1" << std::endl; }

    virtual void v_func1(){ std::cout << "Class C v_func1" << std::endl; }
    virtual void v_func2(){ std::cout << "Class C v_func2" << std::endl; }
    virtual void v_func3(){ std::cout << "Class C v_func3" << std::endl; }

private:
    int m_cMember;   
};

上面的代码中:

  • 父类A中,有属性m_aMember,普通函数func1() func2(),有虚函数 v_func1() v_func2()
  • 父类B中,有属性m_bMember,普通函数func1(),虚函数 v_func1() v_func2() v_func4()
  • class C继承了A B,有属性m_cMember,普通函数func1(),虚函数 v_func1() v_func2() v_func3()

在多继承情况下,有多少个基类就有多少个虚函数表指针,前提是基类要有虚函数。在这里插入图片描述
如图,虚函数表指针01指向的虚函数表是以A的虚函数表为基础的,子类C的虚函数vfunc1() vfunc2() 函数指针覆盖了虚函数表01中的虚函数指针01的位置、02位置。

当子类有多出来的虚函数时,会被添加在第一个虚函数表中,所以,子类C的 v_func3 会被添加到以A虚函数表为基础的第一个虚表中。

当有多个虚函数表时,虚函数表的结果是0代表没有下一个虚函数表。" * "号位置在不同操作系统中实现不同,代表有下一个虚函数表

规则:

  1. 子类虚函数会覆盖每一个父类每一个同名虚函数
  2. 父类中没有的虚函数而子类有,填入第一个虚函数表中
  3. 父类中有,而子类中没有,则不覆盖。
int main(int argc, char* argv[])
{
    class A* a = new class C;
    a->func1();
    a->func2();
    a->v_func1();
    a->v_func2();


    class B* b = new class C;
    b->func1();
    b->v_func1();
    b->v_func2();
    b->v_func4();

    return 0;
}
    

运行结果:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3 Hooks 模块化抽离

Vue3中的Hooks 其实就是业务逻辑的抽离&#xff0c;跟Vue2中mixin 本质上是一样的&#xff1a;将当前组件的业务逻辑抽离到一个公共的文件中&#xff0c;提高逻辑复用性&#xff0c;让当前组件看起来更加清爽&#xff0c;不太一样的地方是我们封装hooks 的时候一般是返回一个函…

如何不改动 GatewayWorker 依赖包下自定义协议

前言&#xff1a; GatewayWorker 是 Workerman 的一个框架&#xff0c;对应用层开发者更友好。GatewayWorker 多了一个网关&#xff0c;也就是 Gateway&#xff0c;负责与客户端连接&#xff0c;消息转发等。而自定义的协议&#xff0c;也就是 gateway 面向客户端提供服务的协议…

java毕业设计——基于java+JDBC+sqlserver的物业管理系统设计与实现(毕业论文+程序源码)——物业管理系统

基于javaJDBCsqlserver的物业管理系统设计与实现&#xff08;毕业论文程序源码&#xff09; 大家好&#xff0c;今天给大家介绍基于javaJDBCsqlserver的物业管理系统设计与实现&#xff0c;文章末尾附有本毕业设计的论文和源码下载地址哦。 文章目录&#xff1a; 基于javaJDB…

【H5微信授权】简单实现H5页面微信授权功能,微信开发者工具报错 系统错误,错误码-1,undefined解决办法【详细】

前言 最近写到了H5公众号&#xff0c;需要微信授权的功能。 这里记录一下授权的流程和踩了个坑 图片 授权代码执行后会跳转到授权的地方&#xff0c;没有授权的会有确认授权&#xff0c;授权过得会这样&#xff0c;直接自动登录&#xff0c;然后再跳转到中转页 授权流程 …

204 - 205.表的基本用法

表的基本操作 1.基本概念 1.1数据库和表的关系 每个数据库包含N张表&#xff0c;及表示在库中 1.2 表&#xff08;二维表&#xff09; 行和列组成&#xff0c;可以将复杂的数据保存在简单的表中 表中的每一行就是一条完整的记录 表中的列用于保存每条记录中特点的信息 2.…

【Redis】散列表(Hash)和列表(List)的运用和理解以及Hash和List应用场景对比详解

文章目录一. 散列表(hash)1.1 基本操作1.2 当value字符串的内容是数字时二.列表&#xff08;List&#xff09;2.1 基本操作三.Hash和List的应用场景3.1Hash的应用场景3.2List的应用场景一. 散列表(hash) Redis哈希是字符串类型字段和值的映射表。哈希特别适合存储对象。 Redis中…

mindspore.dataset的map问题

1、创建一个包含transform的自定义类并实例化 2. 类的实现如下&#xff1a; 请注意&#xff1a;在call函数中&#xff0c;我并没有调用init中定义的transform操作。 3. ImageFolder_forPretrain的定义如下。 在划红线的那行&#xff0c;出现错误。 错误信息&#xff1a; 疑问…

【Spark】spark-submit作业提交及参数设定

note 文章目录note一、Spark的常用启动方式1.1 local本地模式1.2 Standalone模式1.3 Spark on Yarn模式二、spark-submit 详细参数说明--master--deploy-mode--class--name--jars--packages--exclude-packages--repositories--py-files--files--conf PROPVALUE--properties-fil…

【趣学算法】Day3 贪心算法——背包问题

14天阅读挑战赛努力是为了不平庸~ 算法学习有些时候是枯燥的&#xff0c;这一次&#xff0c;让我们先人一步&#xff0c;趣学算法&#xff01; ❤️一名热爱Java的大一学生&#xff0c;希望与各位大佬共同学习进步❤️ &#x1f9d1;个人主页&#xff1a;周小末天天开心 各位大…

tomcat应用部署

目录 tomcta介绍 tomcat安装 每个目录的作用 端口 实验开始 安装&#xff0c;启动MySQL 创建数据库 更改数据库连接 项目导入tomcat和数据库查看 重启tomcat 访问应用 【注意】 tomcta介绍 Tomcat的是完全开源的 Tomcat的是免费的 Tomcat不支持EJB 应用范围&#xff1…

创造一个表格编辑距离指标

这个是我自研的&#xff0c; 与百度PaddleOCR的方式略有不同。 数据的格式&#xff1a; 相当于一个目标检测有两类&#xff0c;分别是table和cell。 在预测值和标签中要先把根据位置关系所有的cell划分到不同的table中。 另外cell标签中还有起止位置 比如 四个数字代表行和…

window10远程桌面控制Ubuntu系统

Windows操作系统作为全球使用最多的个人操作系统&#xff0c;在我们身边随处可见&#xff0c;但放眼各类电子设备的操作系统&#xff0c;windows并不是一家独大&#xff0c;服务器系统大多基于Linux系统开发、手机操作系统几乎都是安卓、更不用说还有苹果的iOS、树莓派、Ubuntu…

【MySQL高级篇】数据库到底是什么?一文带你快速上手MySQL

在学习JavaWeb过程中&#xff0c;数据库学习是不可或缺的。整个JavaWeb体系中&#xff0c;数据库部分用于储存和管理数据&#xff0c;而数据作为网页中非常重要的一部分&#xff0c;自然我们是有必要深入学习数据库的。 推荐学习专栏&#xff1a;Java编程 进阶之路 文章目录1. …

【C语言 数据结构】顺序表的使用

本文借鉴点击跳转 上一篇&#xff1a;线性表的简绍 文章目录顺序表什么是顺序表顺序表的初始化顺序表插入元素顺序表删除元素顺序表 什么是顺序表 顺序表又称顺序存储结构&#xff0c;是线性表的一种&#xff0c;专门存储逻辑关系为“一对一”的数据。 顺序表存储数据的具体…

Linux~一些基本开发工具的使用(yum,vim,gcc,gdb,makefile)

目录 一.yum——安装软件 二.Vim——文本编辑器 &#xff08;1&#xff09;.命令模式 &#xff08;2&#xff09;.底行模式 &#xff08;3&#xff09;.插入模式 tips:给对应用户配置sudo命令 一些注意事项 三.gcc/g——编译器 &#xff08;1&#xff09;.gcc如何完成…

客快物流大数据项目(八十三):Kudu的优化

文章目录 Kudu的优化 一、​​​​​​​Kudu关键配置 二、​​​​​​​​​​​​​​Kudu的使用限制 1、​​​​​​​​​​​​​​主键 2、Cells 3、​​​​​​​字段 4、表 5、其他限制 6、​​​​​​​​​​​​​​分区限制 7、扩展建议和限制 8、​…

2- 创建Spring项目—— 方式1

导包&#xff1a; 地址&#xff1a;https://repo.spring.io/ui/native/libs-release-local/org/springframework/spring/ 如&#xff1a;包名&#xff1a;spring-5.2.3.RELEASE-dist.zip 将其下libs文件夹下的4个jar包导入&#xff1a; spring-context-5.2.3.RELEASE.jar、…

物联网ARM开发-3协议-单总线应用红外遥控

前言&#xff1a;单总线类传感器-红外遥控的实现 目录 一、红外遥控工作原理讲解 1、光的基础知识 2、红外通信 3、红外NEC协议详解 二、红外遥控实例讲解 1、实验目的&#xff1a;按下遥控按键&#xff0c;主机通过红外接收器接收到信号并解码&#xff0c;识别出按键的…

大学生计算机相关专业有什么血泪建议吗?

工作多年后才发现大学期间错过太多&#xff01;&#xff01;&#xff01;给正在攻读CS的你&#xff0c;以下建议&#xff1a; 神级编程网站&#xff0c;堪称程序员的充电站&#xff0c;我给你找好了不能错过_程序员编程指南的博客-CSDN博客_程序员充电网站 大的课程很多&…

LA-PEG-NH2,Lipoic acid-PEG-Amine,硫辛酸PEG氨基用于量子点表面

表面反应性聚乙二醇聚乙二醇化试剂LA-PEG-NH2&#xff0c;又名Lipoic acid-PEG-Amine其中文名为硫辛酸-聚乙二醇-氨基。所属分类为Amine PEG Lipoic acid&#xff08;LA&#xff09;PEG。 该科研试剂Lipoic acid-PEG-Amine的分子量均可定制有&#xff1a;&#xff08;5000、20…