C++回顾(十)—— 多态

news2025/7/22 23:27:26

10.1 问题引出

10.1.1 如果子类定义了与父类中原型相同的函数会发生什么?

  • 函数重写
    在子类中定义与父类中原型相同的函数,函数重写只发生在父类与子类之间

  • 重载与重写区别:
    (1)重载:同一个作用域;
    子类无法重载父类函数,父类同名函数将被覆盖;
    重载是在编译期间根据参数类型和个数决定;

    (2)重写:发生于父类、子类之间;
    父类和子类函数有相同的函数原型;
    使用virtual关键字声明后能够产生多态
    运行期间根据具体对象类型决定调用的函数

在这里插入图片描述
示例代码:

#include <iostream>

using namespace std;

class Parent
{
public:
	void show()
	{
		cout << "this is parent"  << endl;
	}
};

class Child : public Parent
{
public:
	void show()
	{
		cout << "this is Child" << endl;
	}
};

int main()
{
	Parent *p1 = new Child;   //基类指针指向派生类对象
	p1->show();   //静态联编:编译器根据p1的类型(Parent *)调用Parent里面的show函数

	return 0;
}

运行结果:
在这里插入图片描述
问题在于:这里应该是要调用子类的show函数,怎么解决呢?

10.1.2 面向对象新需求

  • 根据实际的对象类型来判断重写函数的调用
    如果父类指针指向的是父类对象则调用父类中定义的函数
    如果父类指针指向的是子类对象则调用子类中定义的重写函数
    在这里插入图片描述

10.1.3 解决方案

  • C++中通过virtual关键字对多态进行支持
  • 使用virtual声明的函数被重写后即可展现多态特性

10.2 多态成立的三个条件

10.2.1 多态的的概念

直观点说,多态就是指相同的语句有不同的执行结果(多态:多种形态)

10.2.2 三个条件

(1)要有继承

(2)要有虚函数的重写

(3)用父类指针(父类引用)指向子类对象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
示例代码:

#include <iostream>

using namespace std;

class Parent
{
public:
	virtual void show()    //被virtual修饰的函数叫虚函数
	{
		cout << "this is parent"  << endl;
	}
};

class Child : public Parent   //1、要有继承
{
public:
	void show()    //2、要有虚函数重写(发生在不同的作用域中,函数原型相同)
	{
		cout << "this is Child" << endl;
	}
};

int main()
{
	Child c;
	Parent p;

	p = c;

	Parent *p1 = new Child;   //3、基类指针指向派生类对象
	p1->show();   //动态联编:运行的时候才能知道p1指向什么对象
	delete p1;
	p1 = new Parent;    //基类指针指向基类对象
	p1->show();     //相同的语句有不同的执行结果(多态:多种形态)
	delete p1;

	return 0;
}

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

10.3 静态联编和动态联编

  • 1、联编是指一个程序模块、代码之间互相关联的过程。
  • 2、静态联编(static binding),是程序的匹配、连接在编译阶段实现,也称为早期匹配。(重载函数使用静态联编)。
  • 3、动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编(迟绑定)。(switch 语句和 if 语句是动态联编的例子)。

补充:

  • 1、C++与C相同,是静态编译型语言
  • 2、在编译时,编译器自动根据指针的类型判断指向的是一个什么样的对象;所以编译器认为父类指针指向的是父类对象。
  • 3、由于程序没有运行,所以不可能知道父类指针指向的具体是父类对象还是子类对象
  • 从程序安全的角度,编译器假设父类指针只指向父类对象,因此编译的结果为调用父类的成员函数。这种特性就是静态联编。

10.4 多态原理实现

在这里插入图片描述

  • 说明1:通过虚函数表指针调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多

  • 说明2:出于效率考虑,没有必要将所有成员函数都声明为虚函数
    在这里插入图片描述
    示例代码:

#include <iostream>

using namespace std;

class Parent
{
public:
	int a;
	virtual void show()
	{
		cout << "thsi is parent" << endl;
	}
};

class Child : public Parent
{
public:
	virtual void show()
	{
		cout << "this is Child" << endl;
	}
};

int main()
{
    Parent pp; 
    Child cc; 

    cout << sizeof(cc) << endl;
    cout << sizeof(pp) << endl;
    cout << "Parent对象的起始地址 " << &pp << endl;
    cout << "成员变量a的起始地址 "  << &pp.a << endl;

	Parent *p = new Child;
	p->show();   //通过指针找到对象,通过对象前8个字节找到虚函数表,通过虚函数表找到对应的函数,调用函数(效率低)
		    //不要将所有函数都声明成虚函数

	delete p;
	p = new Parent;
	p->show();


	return 0;
}

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

10.5 虚析构函数

10.5.1 通过父类指针释放所有的子类资源

  • 在什么情况下应当声明虚函数
    构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数
    析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象

  • 虚析构函数:通过父类指针释放子类对象
    在这里插入图片描述
    在这里插入图片描述
    不用虚析构函数的话,这里释放对象的时候,就不会调用子类的析构函数

示例代码:

#include <iostream>

using namespace std;

class Parent
{
public:
	Parent()
	{
		cout << "Parent构造函数" << endl;
	}
	virtual void show()    //被virtual修饰的函数叫虚函数
	{
		cout << "this is parent"  << endl;
	}

	virtual ~Parent()
	{
		cout << "Parent析构函数" << endl;
	}
};

class Child : public Parent   //1、要有继承
{
public:
	Child()
	{
		cout << "Child构造函数" << endl;
	}
	void show()    //2、要有虚函数重写(发生在不同的作用域中,函数原型相同)
	{
		cout << "this is Child" << endl;
	}
	~Child()
	{
		cout << "Child析构函数" << endl;
	}
};

int main()
{
	Parent *p1 = new Child;  
	p1->show();  
	delete p1;    //释放派生类对象  虚析构函数:通过基类指针释放派生类对象

	return 0;
}

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

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

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

相关文章

A、力扣刷题——数组

提示&#xff1a;这是力扣上数组类题目里的简单题&#xff0c;按顺序做下来的23道题。 第一题&#xff1a; 关键词&#xff1a;原地修改&#xff0c;有序数组 我的答案&#xff1a; &#xff08;for循环&#xff09; &#xff08;1&#xff09;有序数组 class Solution{pu…

平安大视野前瞻2023投资策略:资产格局“危中有机”,关注科技、绿色、安全领域

3月3日&#xff0c;平安私人银行全新云端沙龙《平安大视野》第一站在杭州举办&#xff0c;活动聚焦2023年宏观经济展望&#xff0c;邀请中国首席经济学家论坛理事、鹏扬基金首席经济学家陈洪斌&#xff0c;平安私人银行首席策略分析师彭伟伟就国内外宏观经济变化和资产配置策略…

干货系列:高通量测序后的下游实验验证方法——m6A RNA甲基化篇|易基因

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。此前&#xff0c;我们分享了m6A RNA甲基化研究的数据挖掘思路&#xff08;点击查看详情&#xff09;&#xff0c;进而筛选出m6A修饰目标基因。做完MeRIP-seq测序后&#xff0c;如果需要对分…

【JavaWeb】Servlet详解

文章目录1. 前置知识2.servlet生命周期2.1 默认情况下&#xff0c;服务器启动时&#xff0c;servlet对象并没有被创建2.2 用户执行一次请求2.3用户执行第二次请求2.4 3,4,5,6....次请求2.5 关闭服务器3.servlet方法解析4.适配器模式改造servlet4.1不使用servlet模式4.2使用适配…

【微服务】(十五)—— Seata 的部署和集成

文章目录1. 部署Seata的tc-server1.1 下载Seata1.2 解压1.3 修改配置1.4 在nacos添加配置1.5 创建数据库表1.6 启动TC服务2. 微服务集成seata2.1 引入依赖2.2 修改配置文件3. TC服务的高可用和异地容灾3.1 模拟异地容灾的TC集群3.2 将事务组映射配置到nacos3.3 微服务读取nacos…

软测入门(八)Selenium项目实战

自动化项目实战 项目计划设计 测试计划测试范围设定目标规划活动 实际项目中&#xff0c;需要根据项目的实际情况创建自己的项目计划&#xff0c;没有固定的格式和内容要求&#xff1a; 项目简介自动化实现网上购票项目启动前置条件1.购票网站工作正常 2.自动化测试环境准备…

JAVA的16 个实用代码优化小技巧

一、类成员与方法的可见性最小化 举例&#xff1a;如果是一个private的方法&#xff0c;想删除就删除。 如果一个public的service方法&#xff0c;或者一个public的成员变量&#xff0c;删除一下&#xff0c;不得思考很多。 二、使用位移操作替代乘除法 计算机是使用二进制…

垒骰子(爆搜/DP)

动态规划方格取数垒骰子方格取数 题目描述 设有 NNN \times NNN 的方格图 (N≤9)(N \le 9)(N≤9)&#xff0c;我们将其中的某些方格中填入正整数&#xff0c;而其他的方格中则放入数字 000。如下图所示&#xff08;见样例&#xff09;: A0 0 0 0 0 0 0 00 0 13 0 …

ChatGPT助力校招----面试问题分享(一)

1 ChatGPT每日一题&#xff1a;期望薪资是多少 问题&#xff1a;面试官问期望薪资是多少&#xff0c;如何回答 ChatGPT&#xff1a;当面试官问及期望薪资时&#xff0c;以下是一些建议的回答方法&#xff1a; 1、调查市场行情&#xff1a;在回答之前&#xff0c;可以先调查一…

Python基础之while循环

一&#xff1a;while语法 while 条件:代码1 代码2 代码3....while的运行步骤&#xff1a; 步骤1&#xff1a;如果条件为真&#xff0c;那么依次执行&#xff1a;代码1、代码2、代码3、...... 步骤2&#xff1a;执行完毕后再次判断条件,如果条件为True则再次执行&#…

通用缓存存储设计实践

目录介绍 01.整体概述说明 1.1 项目背景介绍1.2 遇到问题记录1.3 基础概念介绍1.4 设计目标1.5 产生收益分析 02.市面存储方案 2.1 缓存存储有哪些2.2 缓存策略有哪些2.3 常见存储方案2.4 市面存储方案说明2.5 存储方案的不足 03.存储方案原理 3.1 Sp存储原理分析3.2 MMKV存储…

【数据挖掘】4、关联分析:Apriori、FP-Growth 算法、买面包是否也爱买啤酒

文章目录一、概念1.1 支持度1.2 置信度1.3 提升度二、Apriori 算法2.1 频繁项集的定义2.2 算法工作原理三、FP-Growth 算法3.1 算法步骤3.1.1 创建项头表3.1.2 构造 FP 树3.1.3 通过 FP 树挖掘频繁项集3.2 手动推导3.2.1 计算单一项的频率&#xff08;支持度计数&#xff09;3.…

shusheng007编程手记

[版权申明] 非商业目的注明出处可自由转载 出自&#xff1a;shusheng007 文章目录概述工具篇IntelliJ IDEA在Idea中下载源码时&#xff0c;报无法下载源码PostmanPost请求被识别成Get请求Linux开启关闭防火墙开放端口关闭端口如何修复磁盘Nginx如何使用docker来安装Nginx概述 …

VMware虚拟网络编辑桥接/NAT/仅主机模式详解

VMware虚拟网络编辑 安装VMware后 默认虚拟网络设置&#xff1a; VMnet0&#xff1a;桥接模式 VMnet1&#xff1a;仅主机 VMnet8&#xff1a;NAT模式 可以打开VMware的虚拟网络适配器查看 NAT模式 NAT模式借助虚拟NAT设备和虚拟DHCP服务器&#xff0c;使得虚拟机可以联网…

掌握Swagger3自动化生成接口文档完成后端提效

文章目录OpenApi规范Swagger3快速上手Swagger3使用Swagger3.x常用注解讲解和配置Api 模块配置ApiOperation 接口配置ApiParam 方法参数配置ApiIgnore 忽略此接口ApiModel()和ApiModelProperty()ApiResponse描述接口响应注意可能出现的问题OpenApi规范 开放API规范&#xff08;…

Java内存屏障简介

简介 内存屏障是插入两个CPU命令之间的命令&#xff0c;禁止处理器命令的重新排序(如屏障)&#xff0c;以确保有序性。此外&#xff0c;为了达到屏障的效果&#xff0c;在处理器写入、读取值之前&#xff0c;将主机的值写入缓存&#xff0c;清空无效的队列&#xff0c;保障可见…

C++函数重载及其背后的原理

写在前面 先说说我的状态吧&#xff0c;五一假期五天假&#xff0c;这些天都在玩&#xff0c;很少学习&#xff0c;我不是后悔&#xff0c;也没必要&#xff0c;本来假期就是为了让自己放松.我唯一要反思看到别人在学,我心里也想学但是却做不到,这是我的缺点,后面我会克服的.尽…

运维提质增效,有哪些办法可以做

凡是代码&#xff0c;难免有 bug。 开发者们的日常&#xff0c;除了用一行行代码搭产品外&#xff0c;便是找出代码里的虫&#xff0c;俗称 debug。 随着移动互联网的快速发展&#xff0c;App 已经成为日常生活中不可或缺的一部分。但是在开发者/运维人员的眼里简直就是痛苦的…

使用R语言包clusterProfiler做KEGG富集分析时出现的错误及解决方法

使用enrichKEGG做通路富集分析时&#xff0c;一直报错&#xff1a;显示No gene can be mapped....k <- enrichKEGG(gene gene, organism "hsa", pvalueCutoff 1, qvalueCutoff 1)但是之前用同样的基因做分析是能够成功地富集到通路&#xff0c;即便是网上的数据…

Appium+Python连接真机、跳过登录页、Unexpected error while obtaining UI hierarchy问题

Appium连接真机 使用数据线连接电脑&#xff0c;然后选择文件传输方式 打开手机设置拉至底部&#xff0c;点击关于手机&#xff0c;连续点击7次版本号打开开发者模式 点击设置中的系统与更新&#xff0c;找到开发者选项----> 打开USB调试即可 在终端中输入adb devices确定…