C++中多态的使用和建立条件

news2024/11/7 15:28:57

一、多态引入

多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

根据代码,引入多态的概念:

#include <iostream>
using namespace std;

//定义一个成人类
class Person {
public:
	virtual void BuyTicket() { cout << "成人买票-全价" << endl; }
};

//定义一个学生类
class Student : public Person {
public:
	virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person ps;  //成人对象
	Student st; //学生
	Func(ps);  // void Func(Person& p) 参数传参,函数调用
	Func(st);  // void Func(Person& p) 参数传参,函数调用
	return 0;
}

在这里插入图片描述
执行结果可以看出根据对象的不同,调用出来不同的结果

多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会
产生出不同的状态 。

修改上面的代码,观察结果:
#include <iostream>
using namespace std;

class Person {
public:
	//修改前:virtual void BuyTicket() { cout << "成人买票-全价" << endl; }
	 void BuyTicket() { cout << "成人买票-全价" << endl; }
};

class Student : public Person {
public:
	//修改前:virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
	 void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}

在这里插入图片描述
我们去掉virtual 关键字之后,看到结果都是调用父类的BuyTicket()函数。 原因分析:student类继承person类,两个类里面的BuyTicket()函数函数名一样,构成重定义(1.函数名相同 2.两个函数分别在父子类的作用域里 3. 父子类的同名函数不构成重写),所以子类对象赋值给父类对象的时候,会将子类对象中的父类切出来,自然而然调用的是父类的成员函数

我们再修改代码,思考结果:
#include <iostream>
using namespace std;

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

class Student : public Person {
public:
	virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person p)  //修改前:Person& p
{
	p.BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}

在这里插入图片描述
这个结果的产生原因和上面的原因是一致的,父类赋值给父类对象自然调用父类的成员函数、子类对象赋值给父类对象造成“切片”,将子类中的父类那一部分切出去,让父类对象调用

再修改代码,看结果:
#include <iostream>
using namespace std;

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

class Student : public Person {
public:
	virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person* p)  //修改前:Person& p
{
	p->BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(&ps);  //修改前 Func(ps);
	Func(&st);  //修改前 Func(st);
	return 0;
}

在这里插入图片描述

使用指针的时候又根据指向对象的不同,调用出不同的结果

根据上面的代码结果,我们可以大概感知到多态就是根据不同继承关系的类对象,调用同一函数时产生了不同的行为。那么产生多态的条件是什么呢?


文章目录

  • 一、多态引入
        • 修改上面的代码,观察结果:
        • 我们再修改代码,思考结果:
        • 再修改代码,看结果:
  • 二、多态构成的条件
        • 回顾上面的代码,感受多态的条件
        • 析构函数的重写-析构函数名统一会被处理成destructor()
        • C++11 新语法:override 和 final
        • 抽象类
            • 1、概念
            • 2、 抽象类代码感知
  • 总结


二、多态构成的条件

重写的概念

  • 两个函数分别在子类和父类的作用域中
  • 函数名 / 返回值 / 参数都必须相同
  • 两个函数必须是虚函数 (被virtual 修饰的类成员函数成为虚函数
  • 注意:参数只看类型是否相同,不看缺省值

多态构成的条件

  • 需要在继承中使用
  • 必须使用父类的引用或者指针调用虚函数
  • 被调用的函数必须是虚函数,且子类必须对父类的虚函数进行重写
回顾上面的代码,感受多态的条件
#include <iostream>
using namespace std;
class Person {
public:
    //父类的虚函数
	 virtual void BuyTicket() { cout << "成人买票-全价" << endl; }
};

class Student : public Person {
public:
    //子类 重写 父类的虚函数(重写:函数名、返回值、参数必须相同)
	virtual void BuyTicket() { cout << "学生买票-半价" << endl; }
};

void Func(Person* p)
{
	p->BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(&ps);
	Func(&st);
	return 0;
}

这时候指针p,它指向什么类型,就调用该类型的对象里面的成员

在这里插入图片描述

析构函数的重写-析构函数名统一会被处理成destructor()

只有子类 Student 的析构函数重写了 Person 的析构函数,下面的 delete 对象调用析构函
数,才能构成多态,才能保证 p1 和 p2 指向的对象正确的调用析构函数。
函数名处理成destructor() 才能满足多态:
如果父类的析构函数为虚函数,此时子类析构函数只要定义,无论是否加virtual关键字,
都与父类的析构函数构成重写,虽然父类与子类析构函数名字不同。虽然函数名不相同,
看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处
理,编译后析构函数的名称统一处理成destructor。

class Person {
public:
 virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
 virtual ~Student() { cout << "~Student()" << endl; }
};
int main()
{
 Person* p1 = new Person;
 Person* p2 = new Student;
 delete p1;
 delete p2;
 return 0; 
}

在这里插入图片描述
所以当父类的指针指向子类的对象时,一定需要将父类的析构函数写成虚函数,如果不定义成虚函数,那么delete p的时候,就会只父类的析构函数(切片),不会调用子类的析构函数,造成内存泄漏

C++11 新语法:override 和 final

final:修饰虚函数,表示该虚函数不能再被重写

在这里插入图片描述

override: 检查子类虚函数是否重写了父类某个虚函数,如果没有重写编译报错

在这里插入图片描述

抽象类
1、概念

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

2、 抽象类代码感知
#include <iostream>
using namespace std;

class Car
{
public:
	virtual void Drive() = 0;
	//	// 实现没有价值,因为没有对象会调用他
	//	/*virtual void Drive() = 0
	//	{
	//		cout << " Drive()" << endl;
	//	}*/
};
class Benz :public Car
{
public:
	virtual void Drive()  //重写父类的纯虚函数
	{
		cout << "Benz-舒适" << endl;
	}
};
class BMW :public Car
{
public:
	virtual void Drive() //重写父类的纯虚函数
	{
		cout << "BMW-操控" << endl; 
	}
};
void Test()
{
	Car* pBenz = new Benz;
	pBenz->Drive();
	Car* pBMW = new BMW;
	pBMW->Drive();
}
int main()
{
	Test();
	return 0;
}

在这里插入图片描述


总结

今天我们熟悉了多态的构成条件以及多态的使用,下一次我们详细聊聊多态的原理,感知一下为什么会出现这样的结果?编译器是怎么做得?

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

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

相关文章

智能台灯语音控制丨解放双手

台灯是日常生活中一种常见的照明产品。以往的台灯大多都是采取手动控制&#xff0c;通过按键去对台灯的亮度进行调整。随着科技的发展&#xff0c;台灯也开始走向了智能化。人们已经能够对智能台灯进行语音控制&#xff0c;通过调节灯光开关、色温、灯光亮度等操作&#xff0c;…

代码随想录Day24 LeetCode T491 递增子序列 LeetCode T46 全排列 LrrtCode T47 全排列II

LeetCode T491 递增子序列 题目链接:491. 递增子序列 - 力扣&#xff08;LeetCode&#xff09; 题目思路: 首先这里的测试用例很容易误导我们,这道题不能使用上次子集的思路对数组先排序,使用一个used数组来解决问题. 我们用[4,7,6,7]举例这道题的递增序列不存在[4,6,7,7]这个…

“In Global,For China”,许战海咨询LOGO全面焕新

许战海咨询LOGO全面焕新&#xff0c;正式开启全球化战略! 作为一家起源于中国的全球性战略咨询公司&#xff0c;全新LOGO展现许战海咨询国际化企业形象&#xff0c;诠释出许战海咨询最新、最前沿的战略咨询理念“In Global&#xff0c;For China”(在全球&#xff0c;为中国)。…

CCF CSP认证 历年题目自练Day36

题目一 试题编号&#xff1a; 202309-1 试题名称&#xff1a; 坐标变换&#xff08;其一&#xff09; 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题描述&#xff1a; 问题描述 对于平面直角坐标系上的坐标 (x,y)&#xff0c;小 P 定义了一个包含 n 个操作的…

SOFAStack软件供应链安全产品解析——SCA软件成分分析

近年来&#xff0c;软件供应链安全相关攻击事件呈快速增长态势&#xff0c;造成的危害也越来越严重&#xff0c;为了保障软件供应链安全&#xff0c;各行业主管单位也出台了诸多政策及技术标准。基于内部多年的实践&#xff0c;蚂蚁数科金融级云原生PaaS平台SOFAStack发布完整的…

搞一个生成modbus报文的CRC校验码的可视化工具

用python搞个可视化界面&#xff1a; # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QMessageBox# 生成 Modbus 格式的 CRC 校验码 def crc16_modbus(data):crc 0xFFFFfor byte in data:crc …

C++类和对象(二)(类对象的存储方式)

类对象模型 1 如何计算类对象的大小 class A { public: void PrintA() {cout<<_a<<endl; } private: char _a; }; 问题&#xff1a;类中既可以有成员变量&#xff0c;又可以有成员函数&#xff0c;那么一个类的对象中包含了什么&#xff1f;如何计算一个类的大小&…

混淆矩阵绘制

import numpy as np import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix# 示例的真实标签和预测标签 true_labels [cat, dog, bird, cat, dog, bird, cat, bird, bird] predicted_labels [cat, bird, dog, cat, bird, dog, cat, cat, bird]# 确定…

vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法

vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法 先看一下效果图&#xff08;想在表单里动态的增删 form-item&#xff0c;然后添加rules&#xff0c;校验其必填项&#xff1b; &#xff09;: html部分 <div v-for"(item, index) in …

Spring创建复杂对象

目录 一、什么是复杂对象 二、创建复杂对象的3种方式 2.1 实现FactoryBean接口 2.1.1 普通的创建方式 2.1.1 依赖注入的方式 2.1.3 FactoryBean的工作原理 2.2 实例工厂 2.3 静态工厂 一、什么是复杂对象 书接上回&#xff0c;我们已经分析了Spring是怎么去创建对象的了。那什…

【公众号开发】访问第三方接口应用于开发 · 回复图文消息

【公众号开发】&#xff08;2&#xff09; 文章目录 【公众号开发】&#xff08;2&#xff09;1. 第三方接口1.1 申请免费接口1.2 解读接口文档1.3 postman测试接口1.4 公众号开发访问第三方接口原理1.5 访问第三方接口示例1.5.1 引入依赖1.5.2 获取form格式的body字符串的方法…

EPLAN_005#宏边框、页宏、窗口宏/符号宏

一、宏边框 红边框不能用&#xff0c;变成了灰色 要在项目属性中更改位宏项目——才能使用宏边框功能 注意&#xff1a;创建宏边框时候要打开——显示隐藏元素 框选目标后&#xff0c;双击红边框的边——弹出红边框创建属性对话框——输入名称——更改变量ABC等 最后——自动…

10个设计人士应该关注的国内外资源网站

设计师网站1&#xff1a;即时设计 即时设计内拥有上万款来自于优秀设计师的精美设计作品&#xff0c;包括设计规范、页面、插画、图标、产品原型、作品集等等&#xff0c;这些作品往往都是由大厂团队精心总结的设计规范&#xff0c;对应着完善的设计系统与配套组件库。除此之外…

innoDB如何解决幻读

Mysql的事务隔离级别 Mysql 有四种事务隔离级别&#xff0c;这四种隔离级别代表当存在多个事务并发冲突时&#xff0c;可能出现的脏读、不可重复读、幻读的问题。其中 InnoDB 在 RR 的隔离级别下&#xff0c;解决了幻读的问题 事务隔离级别脏读不可重复读幻读未提交读&#xff…

openEuler 服务器安装 JumpServer (all-in-one 模式)

openEuler 服务器安装 JumpServer JumpServer 简介什么是 JumpServer &#xff1f;JumpServer 的各种类型资产JumpServer 产品特色或优势JumpServer 符合 4A 规范 JumpServer 系统架构应用架构组件说明 JumpServer 安装部署环境要求网络端口网络端口列表防火墙常用命令 在线脚本…

上万份订单里,读懂中国互联网企业ESG

【潮汐商业评论/原创】 “残障到底意味着什么&#xff1f;”知乎上有个高赞回答提到&#xff0c;对于大多数残障者而言&#xff0c;他们和家人鼓足了干劲、费劲了心思&#xff0c;只为过上“正常的生活”。 但我们可曾想过&#xff1a;这个世界有没有一开始就准备好接纳所有降…

记一次TheadLocal使用方式不正确导致内存泄漏问题的排查和修复过程

一、背景 一个部门其他同事的上线了很久的项目近期频繁的内存溢出——几乎每天内存溢出一次&#xff0c;而且频率越来越高。在添加了进程守护之后&#xff0c;虽然可以在内存溢出后自动重启&#xff0c;但并没有解决内存溢出的问题。不甘其扰之后&#xff0c;决定仔细排查导致内…

C++string的模拟实现

CSDN的uu们&#xff0c;大家好。这里是C入门的第十六讲。 座右铭&#xff1a;前路坎坷&#xff0c;披荆斩棘&#xff0c;扶摇直上。 博客主页&#xff1a; 姬如祎 收录专栏&#xff1a;C专题 目录 1. string类的成员变量 2. 构造函数 3. 析构函数 4. const char* c_str(…

ESP32网络开发实例-连接信号最强的热点

连接信号最强的热点 文章目录 连接信号最强的热点1、软件准备2、硬件准备3、代码实现在本文中,将向您展示如何使用 ESP32 WiFiMulti 库。 这使我们能够使用多个网络,ESP32 可以连接到列表中可用的最强 WiFi 网络。 每当它失去连接时,它都会重新连接到列表中下一个最强的网络…

数据结构-----图(Graph)论必知必会知识

目录 前言 图的基本概念 1.什么是图&#xff1f; 2 .图的相关术语 3 .有向图和无向图 4.简单图和多重图 5.连通图、强连通图、非连通图 6.权与网 7.子图和(强)连通分量 8.生成树和生成森林 前言 今天我们学习一种新的数据结构-----图&#xff0c;大家在日常生活中经常都…