C++第三大特性:多态(1)

news2024/12/28 19:24:57

目录

   一.多态的含义

1.普通调用:

2.多态调用 

        重写函数:

实现多态调用的三个条件:(缺一不可)

        情况1:当只有父类中存在虚函数,两个子类都没有virtual形成的虚函数时,也能形成多态!

        特殊情况2:协变——也可以形成多态


   一.多态的含义

        多态!通俗来说,就是多种形态,具体上就是去完成某个行为,即当不同的对象去完成某个行为时会产生出不同的状态。而想要体现多态的特性,前提就需要用到继承特性!


        举个栗子:比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时是优先买票。

        想要实现各类对象做某件事时产生不同的状态,有两种方式,一种是普通调用,另一种是多态调用。

1.普通调用:

class Person {
public:
	 void Func() {
		cout << "Person买票——全价" << endl;
	}
};

//子类1:
class Student :public Person{
public:
	 void Func() {
		cout << "Student买票——半价" << endl;
	}
};

//子类2:
class Soldier :public Person {
public:
	 void Func() {
		cout << "Soldier买票——免费" << endl;
	}
};

int main() {
	Person p1;
	Student st1;
	Soldier so1;

	//下面为普通调用,都是各类调用各类的函数
	p1.Func();
	st1.Func();
	so1.Func();
	return 0;
}

运行结果: 

2.多态调用 

class Person {
public:
	void virtual  Func() {
		cout << "Person买票——全价" << endl;
	}
};

class Student :public Person {
public:
	void virtual  Func() {
		cout << "Student买票——半价" << endl;
	}
};

class Soldier :public Person {
public:
	void virtual  Func() {
		cout << "Soldier买票——免费" << endl;
	}
};

void Fun1(Person& p) {
	p.Func();
}

void Fun2(Person* p) {
	p->Func();
}

int main() {
	Person p1;
	Student st1;
	Soldier so1;

	//多态调用
	Fun1(p1);
	Fun1(st1);
	Fun1(so1);

	//多态调用
	Fun2(&p1);
	Fun2(&st1);
	Fun2(&so1);

	return 0;
}

        我们发现在上方代码中,父类与子类的同名成员函数上都加上了virtual关键字,之前我们学习多继承的过程中,了解到virtual关键字的用法是用于解决菱形继承的二义性问题。而今天见到的virtual它又有了新的作用:虚函数,只要在类中加入virtual就代表了该函数是虚函数。 

        而虚函数的作用就是用于实现多态特性。

重写函数:

        重写函数得基于继承的前提下,父类子类都有相同名称、相同参数、相同函数返回值的虚函数叫做重写函数。子类中有一个跟父类完全相同的虚函数(即子类虚函数与父类虚函数的(返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了父类的虚函数。

        如上代码中,子类中的Func函数就是重写函数(重写了父类的Func函数)。重写,从字面意思上看就是重新编写这个从父类继承过来的Func函数,意味着子类的Func函数体中的内容与父类的Func函数是不一样的,这样是实现多态特性的一个重要因素。

        继续回到代码解析中,在上方测试代码中,创建了三类对象,然后对一个形参是父类引用的Fun1函数进行不同实参的调用, 通过调用同一个函数,调用参数不同,实现的结果也不同——这就是多态的特性。

        为什么调用同一个函数,而参数不同,最终实现的结果就不同呢?

        有两个原因:原因1在于Func函数的形参:Person& p  这是父类的引用!!! 

        之前在讲继承的时候讲过一个子类对象可以向上转换赋值父类对象,C++继承——子类对象对父类对象的赋值操作。子类对象之所以能够向上转换赋值父类对象是因为切片,子类对象中包含了父类继承过来的父类成员变量,子类对象在向上转换时切割出父类继承过来的成员变量,之后子类对象就可以将切割出来的成员变量赋值给父类对象——完成了子类对象切割转换赋值父类对象的操作因为person& p ,在main中既可以用Person类的对象做实参,也可以用子类Student/Soldier 的对象做实参,最终完成多态。

 参数的不同实现导致了不同形式的生成。

实现多态调用的三个条件:(缺一不可)


       1.使用virtual关键字修饰父子类的某一同名函数为虚函数
       2.虚函数需要实现三同(同名、同参数、同返回值)——重写函数
       3.多态调用的函数形参使用父类引用/指针 

但在写代码的过程中我们发现:

        情况1:当只有父类中存在虚函数,两个子类都没有virtual形成的虚函数时,也能形成多态!

class Person {
public:
	void Func() {
		cout << "Person买票——全价" << endl;
	}
};

class Student :public Person {
public:
	void Func() {
		cout << "Student买票——半价" << endl;
	}
};

class Soldier :public Person {
public:
	void Func() {
		cout << "Soldier买票——免费" << endl;
	}
};

        对于子类中不加virtual的函数,运行测试案例时,也能够实现多态特性。 

         如上可知,只有父类Person中有虚函数时,最终调用Fun1也能够完成多态,这是因为子类Student/soldier继承了父类的成员,其中也包括了父类的成员函数,那么即使子类中不亲自写出相同名称类型返回值的虚函数也可以,有继承过来的成员函数。

          
        但是当父类中没有加virtual,而子类Student中虽然有virtual,但不能形成多态了,可以试试,结果就会变成3个句子一样,都是调用的Person的Func函数了。


        特殊情况2:协变——也可以形成多态

协变:三同函数中,返回值可以不同,但是要求返回值必须是一个父子类关系的指针或者引用。

代码如下:

class Person {
public:
	virtual Person*   Func() {
		cout << "Person买票——全价" << endl;
		return nullptr;
	}
};

class Student :public Person {
public:
	virtual Student*  Func() {
		cout << "Student买票——半价" << endl;
		return nullptr;
	}
};

class Soldier :public Person {
public:
	virtual Soldier*  Func() {
		cout << "Soldier买票——免费" << endl;
		return nullptr;
	}
};
  

其他代码不变,运行测试用例:

         由上图代码可知:父类的虚函数的返回值变成了Person* ,子类的虚函数返回值分别是Student* 和Soldier*类型的,仍然形成了多态。

        对于违反协变的,编译器都会报编译错误!如下:


 

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

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

相关文章

测试开发人员如何进行局部探索性测试?一张图告诉你

我们都知道全局探索性测试的漫游测试法&#xff0c;也知道局部探索性测试可以从用户输入、状态、代码路径、用户数据和执行环境测试着手点。 那么&#xff0c;如果我们能够获取开发代码&#xff0c;我们怎么从代码入手&#xff0c;进行具体的局部探索性测试呢&#xff1f; 简单…

1227. 分巧克力(简单,易懂)

输入样例&#xff1a; 2 10 6 5 5 6输出样例&#xff1a; 2 这个题就是基础的二分问题&#xff0c;做题思路&#xff1a; 找到一个数&#xff0c;让其满足&#xff0c;所有小块的边值&#xff0c;且最终的总和要大于等于我们的K 第一次做错了&#xff01;&#xff01; #in…

使用Canvas制作画板

使用Canvas制作画板 在本篇技术博客中&#xff0c;我们将使用JavaScript和Canvas技术来创建一个简单的画板应用程序。这个画板将允许用户在一个画布上绘制线条&#xff0c;使用橡皮擦擦除绘制的内容&#xff0c;更改线条的颜色和宽度&#xff0c;并支持撤销和重做功能。 准备…

Html5播放器按钮在移动端变小的问题解决方法

Html5播放器按钮在移动端变小的问题解决方法 用手机浏览器打开酷播云视频&#xff0c;有时会出现播放器按钮太小的情况&#xff0c;此时只需在<head>中加入下面这段代码即可解决&#xff1a; <meta name"viewport" content"widthdevice-width, initia…

矩阵中的路径(JS)

矩阵中的路径 题目 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是…

CopyTranslator-复制即翻译 文献翻译神器 支持多接口翻译

一、软件起源 科研人员总少不了阅读大量文献&#xff0c;理解文献内容就成了科研生活常态&#xff0c;而我们平时复制PDF内容黏贴到网页翻译的时候可能会出现多余换行而导致翻译乱码&#xff0c;译文与中文阅读习惯不符的情况&#xff0c;翻译结果很差&#xff0c;需要手动删除…

2.4 传统经验光照模型详解

一、光照模型 光照模型&#xff08;illumination model&#xff09;&#xff0c;也称为明暗模型&#xff0c;用于计算物体某点处的光强&#xff08;颜色值&#xff09;。从算法理论基础而言&#xff0c;光照模型分为两类&#xff1a;一种是基于物理理论的&#xff0c;另一种是…

《深入浅出Java虚拟机》AIC松鼠活动第五期

1、JAVA虚拟机 1.1什么是java虚拟机 Java虚拟机&#xff08;Java Virtual Machine&#xff0c;JVM&#xff09;是一种用于执行Java字节码的虚拟机。它可以将Java源代码编译为字节码&#xff0c;然后在不同的操作系统和硬件平台上运行。作为Java语言的核心组成部分&#xff0…

MySQL和Oracle区别

由于SQL Server不常用&#xff0c;所以这里只针对MySQL数据库和Oracle数据库的区别 (1) 对事务的提交 MySQL默认是自动提交&#xff0c;而Oracle默认不自动提交&#xff0c;需要用户手动提交&#xff0c;需要在写commit;指令或者点击commit按钮 (2) 分页查询 MySQL是直接在SQL…

挖了个漏洞,挣了¥12000!

今天给大家分享一个挖漏洞致富的事情。 0x01 前言 本人&#xff0c;一个热爱生活&#xff0c;热爱网络安全的小青年。在此记录自己日常学习以及漏洞挖掘中的奇思妙想&#xff0c;希望能与热爱网络安全的人士一起交流共勉。 0x02 漏洞背景 一个app&#xff0c;开局一个登录框…

模拟面试题

面试题一 C# 1. 装箱和拆箱是什么&#xff1f; 装箱是把栈空间的数据转移到堆空间上去&#xff0c;值类型传引用类型上去 int i 2; object o i; 拆箱是把堆空间的数据转移到栈空间上去&#xff0c;引用类型传到值类型上去 i (int) o 2. 值和引用类型在变量赋值时的区别是…

在OK3588板卡上部署模型实现人工智能OCR应用

一、主机模型转换 我们依旧采用FastDeploy来部署应用深度学习模型到OK3588板卡上 进入主机Ubuntu的虚拟环境 conda activate ok3588 安装rknn-toolkit2&#xff08;该工具不能在OK3588板卡上完成模型转换&#xff09; git clone https://github.com/rockchip-linux/rknn-to…

气象名词解释

文章目录 SAMPSAAMO SAM SAM(Southern Annualr Mode) 南半球环状模&#xff0c;是南半球大气环流和气候变异的一种重要现象。具有如下特点&#xff1a; 主要特点&#xff1a; 赤道附近环流&#xff1a;在 SAM 正相位期间&#xff0c;赤道附近的环流增强&#xff0c;称为正 SA…

Java源码规则引擎:jvs-rules决策流的自定义权限控制

规则引擎用于管理和执行业务规则。它提供了一个中央化的机制来定义、管理和执行业务规则&#xff0c;以便根据特定条件自动化决策和行为。规则引擎的核心概念是规则。规则由条件和动作组成。条件定义了规则适用的特定情况或规则触发的条件&#xff0c;而动作定义了规则满足时要…

IO进程线程day3(2023.7.31)

一、Xmind整理&#xff1a; 文件描述符概念&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;用fread和fwrite实现文件拷贝 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <head.h> int main(int argc, const char…

12-3_Qt 5.9 C++开发指南_创建和使用静态链接库

第12章中的静态链接库和动态链接库介绍&#xff0c;都是以UI操作的方式进行&#xff0c;真正在实践中&#xff0c;可以参考UI操作产生的代码来实现同样的功能。 文章目录 1. 创建静态链接库1.1 创建静态链接库过程1.2 静态链接库代码1.2.1 静态链接库可视化UI设计框架1.2.2 qw…

八大排序算法--快速排序(动图理解)

快速排序 概念 快速排序是对冒泡排序的一种改进。其基本原理是通过选取一个基准元素&#xff0c;将数组划分为两个子数组&#xff0c;分别对子数组进行排序&#xff0c;最终实现整个数组的有序排列。快速排序的时间复杂度最好为O(nlogn)&#xff0c;最坏为O(n^2)&#xff0c;…

5个值得收藏的AI绘画网站,还有国产!

随着科技的发展&#xff0c;设计领域也迎来了科技创新&#xff0c;AI绘画网站便是其中的一个代表&#xff0c;本文精选了4个好用的AI绘画网站与大家分享&#xff0c;一起来看看吧&#xff01; 1、即时灵感 作为一个国产的AI绘画网站&#xff0c;即时灵感支持设计师使用中文语…

Spring | Bean 作用域和生命周期

一、通过一个案例来看 Bean 作用域的问题 Spring 是用来读取和存储 Bean&#xff0c;因此在 Spring 中 Bean 是最核心的操作资源&#xff0c;所以接下来我们深入学习⼀下 Bean 对象 假设现在有⼀个公共的 Bean&#xff0c;提供给 A 用户和 B 用户使用&#xff0c;然而在使用的…

IO流简述

IO流IO流使用场景 什么是IO流常用的IO流字节流字符流缓冲流 BIO、NIO、AIO的区别 IO流 IO流使用场景 如果操作的是纯文本文件&#xff0c;优先使用字符流如果操作的是图片、视频、音频等二进制文件。优先使用字节流如果不确定文件类型&#xff0c;优先使用字节流。字节流是万能…