【C++第十二课-多态】多态概念、多态原理

news2024/9/21 16:42:35

目录

  • 多态的概念
    • 概念
    • 多态形成的条件
      • 虚函数的重写
      • 虚函数重写的两个例外
    • 多态的题目
    • C++11增加的关于多态的关键字
      • final
      • override
  • 多态原理
    • 虚函数表指针 vfptr
    • 多态的实现
    • 静态绑定和动态绑定
    • 打印虚函数表
    • 补充
  • 抽象类
    • 概念
    • 接口继承和实现继承

多态的概念

概念

具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。-- 这种场景就可以使用多态
多态调用的情况下,是实现重写

多态形成的条件

虚函数的重写

virtual在继承中是虚继承,用这个关键字修饰函数就是虚函数
virtual只能修饰成员函数

父子类中两个虚函数,三同(函数名,参数,返回值),此时构成虚函数的重新
父类的指针或引用去调用虚函数,传的是父类掉父类的,传子类掉子类的(切片)

class Person
{
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student :public Person
{
public:
	virtual void BuyTicket() { cout << "买票-75折" << endl; }
};

class Soldier :public Person
{
public:
	virtual void BuyTicket() { cout << "买票-免费" << endl; }
};
void Func(Person& p)
{
	p.BuyTicket();
}


int main()
{
	Person p1;
	cout << "Person:";
	Func(p1);
	Student stu;
	cout << "Student:";
	Func(stu);
	Soldier sol;
	cout << "Soldier:";
	Func(sol);
	return 0;
}

在这里插入图片描述

虚函数重写的两个例外

(1)协变:虚函数返回值可以不同,这个返回值要求必须是父子类关系的指针或引用

不满足重写就是隐藏
重载:在同一作用域,函数名相同参数不同,与返回值无关

class A {

};
class B :public A
{

};
class Person
{
public:
	virtual A* BuyTicket() 
	{
		cout << "买票-全价" << endl;
		return new A;
	}
};
class Student :public Person
{
public:
	virtual B* BuyTicket() 
	{ 
		cout << "买票-75折" << endl;
		return new B;
	}
};

void f2()
{
	Person* p = new Student;
	p->BuyTicket();
}

int main()
{
	f2();
	return 0;
}

在这里插入图片描述
(2)析构函数的重写(基类和派生类析构函数的名字不同)
析构函数的名字的特殊处理,及其析构函数建议加virtual
下面这样的析构是正确的
在这里插入图片描述
下面这种情况按道理来说应该是
在这里插入图片描述

在这里插入图片描述

这样会造成p2所指向的内容会有内存泄漏的风险

普通调用:看指针或引用或对象的类型
多态调用:看指针或引用指向的对象
上图的情况适合多态调用,看指针指向的对象去进行delete

在这里插入图片描述
(3)虚函数重写时,父类函数加了virtual,子类不加也构成重写

BuyTicket()在父类中没有virtual,在子类中有virtual。但这样不是重写,只构成了隐藏

在这里插入图片描述
父类的BuyTicket()有virtual,子类的没有,也可以构成重写
函数的接口继承了下来,只重写了实现,与父类形成了重写
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

多态的题目

1、
在这里插入图片描述

C++11增加的关于多态的关键字

final

1、修饰虚函数,表示该虚函数不能再被重写
在这里插入图片描述

2、实现一共类,这个类不能被继承
(1)父类构造函数私有化,派生实例化不出对象

只对成员函数等私有化是不可以的,公有继承碰私有,只是不可见,还是可以被继承,只有再下一代才不能被继承

B类对象实例化的时候需要调用A类的构造函数,但A类构造函数私有化

在这里插入图片描述
(2)C++11,final修饰的类为最终类,不能被继承
在这里插入图片描述

override

检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

在这里插入图片描述

多态原理

虚函数表指针 vfptr

Person类的成员变量只有int,但其大小确实8?
在这里插入图片描述

因为Person类里面有虚函数,类里面会多一个虚函数表指针(虚表指针)指向虚函数表

虚函数表实际上是个指针数组

在这里插入图片描述

多态的实现

对于子类来说,他对BuyTicket函数进行了重写,同时更改了虚函数表里对应的内容

虚函数的重写也叫做覆盖
重写是语法层的概念
覆盖是原理层的概念

在这里插入图片描述
在这里插入图片描述

多态调用:运行时去指向对象的虚函数表中找函数的地址,进行调用,所以指向父类调的是父类虚函数,指向子类调用的是子类虚函数
普通调用:编译时,符号表找到函数地址,通过调用者类型确定函数地址

静态绑定和动态绑定

1.静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载
2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。

打印虚函数表

class Base
{
public:
	virtual void func1()
	{
		cout << "Base::func1()" << endl;
	}
	virtual void func2()
	{
		cout << "Base::func2()" << endl;
	}
private:
	int _i = 1;
};
class Drive :public Base
{
public:
	virtual void func1()
	{
		cout << "Drive::func1()" << endl;
	}
	virtual void func3()
	{
		cout << "Drive::func3()" << endl;
	}
private:
	int _j = 1;
};

typedef void(*VF_ptr)();
void PrintVFT(VF_ptr vft[])
{
	printf("%p\n", vft);
	for (size_t i = 0; vft[i] != nullptr; i++)
	{
		printf("[%d]:%p", i, vft[i]);
		VF_ptr f = vft[i];
		f();
	}
}

int main()
{
	Base bb;
	Drive dd;
	Base* pb = &bb;
	Drive* pd = &dd;
	PrintVFT((VF_ptr*)(*(int*)pd));
	return 0;
}

在这里插入图片描述

补充

1、虚函数表存放在哪个区域(栈、堆、静态区/全局数据段、常量区/只读数据段)?常量区
2、虚表是编译时生成的
3、虚表指针构造函数的初始化列表最开始时生成的
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
Base1里面有个虚表指针,Base2里面有个虚表指针。Drive里面共有两张虚表。
func3()倾向于放在Base1的虚表或者两个虚表都放

抽象类

概念

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

纯虚函数的使用
1、某些情况下不希望父类生成派生类
2、强制要求派生类对于某个虚函数进行重写

在这里插入图片描述

class car
{
public:
	virtual void drive() = 0;
};

class BMW :public car
{
public:
	virtual void drive() { cout << "BMW->操作" << endl; }
};

class Benz :public car
{
public:
	virtual void drive() { cout << "Benz->舒适" << endl; }
};

接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。

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

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

相关文章

Centos安装Kafka

安装Kafka 安装Java&#xff0c;因为Kafka运行需要JDK运行环境 sudo yum install java-1.8.0-openjdk-devel.x86_64如果不知道JDK版本&#xff0c;可以使用下面命令查看可用的JDK版本 sudo yum search openjdk添加EPEL仓库 sudo yum install epel-release下载Kafka 先去查看…

nginx初步学习

Nginx 安装 官方源码包下载地址&#xff1a;nginx: download 配置一台虚拟机尽量给的配置高些这样速度快些。 下载文件并解压 加载对应模块 ./configure --prefix/usr/local/nginx \ --usernginx \ # 指定nginx运行用户 --groupnginx \ # 指定ng…

BUUCTF 极客大挑战2019 Upload 1

上传图片&#xff0c;然后抓包 我们可以看到回显 我们改一下content-type 这里我们可以改一下filename为100.phtml&#xff0c;也可以不改 然后我们通过该指令查看一下是否被解析 我们发现flag就在这里 我们cat一下&#xff0c;得到了flag

kubernetes集群下部署mysql 8.0.20单机数据库

一、背景&#xff1a; 因为业务需求&#xff0c;需要在kubernetes集群下部署一个mysql数据库 8.0.20版本的单机服务。 具体实施过程如下&#xff1a; 二、实施部署mysql数据库&#xff1a; mysql 8.0.20的镜像&#xff1a; dockerhub.jiang.com/jiang-public/mysql:8.0.20-stjh…

Windows有哪些免费好用的PDF编辑器推荐?

不是所有PDF编辑器都免费&#xff0c;但我推荐的这3个一定免费简单好用&#xff01;&#xff01; 1、转转大师PDF编辑器 点击直达链接>>pdftoword.55.la 转转大师PDF编辑器是一款专业的PDF编辑工具&#xff0c;功能丰富&#xff0c;操作简单&#xff0c;作为微软office…

ZYNQ—vitis—网口传输信号波形数据

ZYNQ—vitis—网口传输信号波形数据 工程功能&#xff1a;ADC采集信号&#xff0c;将波形数据通过BRAM传输到PS端&#xff0c;然后用UDP以太网发送。&#xff08;附加&#xff1a;ILA观察信号&#xff0c;发送的数据包含帧头&#xff0c;&#xff09; FPGA端——用BRAM将信号传…

如何在一个页面上定位多个关键词?

我应该针对多个关键词优化我的页面吗&#xff1f; 对于大多数网站来说&#xff0c;答案都是肯定的。 如果你制定的策略是仅针对一个关键字优化你的网页&#xff0c;这可能会导致一些问题。例如&#xff0c;如果一个网页只能使用一个关键字&#xff0c;那么他们可能会开发出非…

vue2利用html2canvas+jspdf动态生成多页PDF

业务需求中&#xff0c;前端把页面上的内容导出为图片&#xff0c;pdf&#xff0c;excel是常有的事。当然&#xff0c;这些工作后端也是能做。秉着前端是万能的理念&#xff0c;今天就站在前端的角度上&#xff0c;来实现将页面内容导出为pdf&#xff0c;实现指定div内容导出&a…

【数据结构篇】~复杂度

标题【数据结构篇】~复杂度 前言 C语言已经学完了&#xff0c;不知道大家的基础都打得怎么样了&#xff1f; 无论怎么说大家还是要保持持续学习的状态&#xff0c;来迎接接下来的挑战&#xff01; 现在进入数据结构的学习了&#xff0c;希望大家还是和之前一样积极学习新知识…

ESP32人脸识别开发--人脸识别模型(六)

ESP-DL ESP-DL 为**神经网络推理**、**图像处理**、**数学运算**以及一些**深度学习模型**提供 API&#xff0c;通过 ESP-DL 能够快速便捷地将乐鑫各系列芯片产品用于人工智能应用 ESP-DL 无需借助任何外围设备&#xff0c;因此可作为一些项目的组件&#xff0c;例如可将其作…

使用Python制作贪吃蛇小游戏

引言 贪吃蛇游戏是一款经典的电子游戏&#xff0c;玩家通过控制一条不断增长的蛇在格子内移动&#xff0c;并吃掉随机出现的食物来获得分数。随着分数的增加&#xff0c;蛇的身体也会越来越长&#xff0c;游戏的难度也随之提升。在本文中&#xff0c;我们将详细介绍如何使用Py…

天途推出无人机软硬件定制服务

近年&#xff0c;随着低空经济高速发展&#xff0c;无人机已成为千行百业的生产和工作辅助工具&#xff0c;很多行业用户都有信息化数字化的软件需求&#xff0c;以及不同负载的集成设计需求。 天途拥有云平台开发、自主飞控系统、控制算法、无人机AI数据系统等核心技术研发团队…

Flink常见面试题整理

Flink常见面试题整理 文章目录 Flink常见面试题整理1. 数据倾斜问题怎么解决&#xff1f;2、什么是Flink中的窗口3、Flink中的水位线&#xff08;Watermarks&#xff09;是什么&#xff1f;4、Flink中的定时器实现机制是什么&#xff1f;有什么作用&#xff1f;5、Flink中的状态…

llama3.1本地部署方式

llama3.1 资源消耗情况 &#xfeff;Llama 3.1 - 405B、70B 和 8B 的多语言与长上下文能力解析&#xfeff; &#xfeff; 70B版本&#xff0c;FP1616K token需要的资源约为75G&#xff1b;FP16128K token需要的资源约为110G &#xfeff; 1、ollama ollama工具部署及使用…

力扣面试经典算法150题:找出字符串中第一个匹配项的下标

找出字符串中第一个匹配项的下标 今天的题目是力扣面试经典150题中的数组的简单题: 找出字符串中第一个匹配项的下标 题目链接&#xff1a;https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/?envTypestudy-plan-v2&envIdto…

免费远程控制电脑的软件有哪些?

什么是远程控制&#xff1f; 远程控制是一种通过网络从一台设备操作另一台设备的技术。连接后&#xff0c;用户可以直接远程操作那台电脑进行各种操作。随着科技的不断进步和用户需求的增加&#xff0c;远程控制市场日益蓬勃。远程控制不仅应用于远程办公和远程教学&#xff0…

Windows11下wsl闪退的解决

wsl闪退 1. 原因分析 解释&#xff1a;WSL&#xff08;Windows Subsystem for Linux&#xff09;闪退通常指的是在Windows操作系统中运行的Linux环境突然关闭。这可能是由于多种原因造成的&#xff0c;包括系统资源不足、WSL配置问题、兼容性问题或者是Linux内核的问题。&…

STM32—PWR电源控制

1.PWR简介 PWR负责管理STM32内部的电源供电部分&#xff0c;可以实现可编程电压监测器和低功耗模式的功能 可编程电压监测器(PVD)可以监控VDD电源电压&#xff0c;当VDD下降到PVD阀值一下或上升到PVD阀值之上时&#xff0c;PVD会触发中断&#xff0c;用于执行紧急关闭任务 低…

HamronyOS开发5.0【埋点】方案讲解

大多数软件产品上线前&#xff0c;都会采用有规则的日志来对软件进行相关数据的采集&#xff0c;这个过程称为&#xff1a;[埋点]&#xff0c;采集的数据主要用于产品分析。 埋点技术已在PC端, 移动端非常成熟&#xff0c;并且有大批量以此为生的公司。 本篇将探究一下Harmon…

STM32—RTC实时时钟

1.Unix时间戳 Unix时间戳定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数&#xff0c;不考虑闰秒 时间戳存储在一个秒计数器中&#xff0c;秒计数器为32位/64位的整形变量 世界上所有时区的秒计数器相同&#xff0c;不同时区通过添加偏移来得到当地时间 2.时间戳转…