【C++】一文简练总结【多态】及其底层原理&具体应用(21)

news2024/11/23 19:32:38

前言

大家好吖,欢迎来到 YY 滴C++系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁
主要内容含:
在这里插入图片描述

欢迎订阅 YY滴C++专栏!更多干货持续更新!以下是传送门!

目录

  • 一.多态的概念
  • 二.多态的实现
    • 1)虚函数&虚函数表
    • 2)虚函数的重写(覆盖)
    • 3)多态的构成条件
    • 4)虚函数重写的两种特殊情况:
        • 【1】协变:(基类与派生类虚函数返回值类型不同)
        • 【2】析构函数的重写:(基类与派生类析构函数的名字不同)
  • 三.【override】【final】关键字——帮助用户检测是否重写(C++11)
      • 【1】 final:表示虚函数不能被重写,被重写即报错
      • 【2】override:检查虚函数是否重写了别的虚函数,重写了即报错
  • 四. 多态的具体应用:抽象类(接口类)(纯虚函数类)
      • 1)利用 [ 只有重写纯虚函数 派生类才能实例化出对象 ] 性质
      • 2)实现继承与接口继承

一.多态的概念

  • 多态是在不同继承关系的类对象,去调用 同一 函数,产生了 不同 的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。
  • 例:iphone和安卓手机用户打车同程不同价

二.多态的实现

1)虚函数&虚函数表

  • 虚函数:即被 virtual 修饰的类成员函数称为虚函数。
class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
  • 虚函数表本质是一个存虚函数指针 指针数组,一般情况这个数组最后面放了一个nullptr。
  • 虚函数表:虚函数表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚函数表中。
  • 一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表

2)虚函数的重写(覆盖)

  • 虚函数的重写(覆盖): 派生类中有一个跟基类完全相同的虚函数 (即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同) ,称子类的虚函数 重写 了基类的虚函数。

3)多态的构成条件

  1. 必须通过 基类的指针 引用 调用虚函数
  2. 被调用的函数 必须是虚函数,且 派生类必须对基类的虚函数进行重写
                                           //多态条件2:被调用的函数 必须是虚函数
class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:                                   //多态条件2:派生类必须对基类的虚函数进行重写
 virtual void BuyTicket() { cout << "买票-半价" << endl; }
/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因
为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议
这样使用*/
/*void BuyTicket() { cout << "买票-半价" << endl; }*/
};


void Func(Person& p)       //多态条件1:必须通过基类的指针来“引用”调用虚函数
{ 
p.BuyTicket(); 
}


int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
 return 0;
}

4)虚函数重写的两种特殊情况:

【1】协变:(基类与派生类虚函数返回值类型不同)
  • 派生类重写基类虚函数时 ,与基类虚函数 返回值类型不同 。即如下代码所示:【基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时】,称为协变
class A{};
class B : public A {};
class Person {
public:
 virtual A* f() {return new A;}
};
class Student : public Person {
public:
 virtual B* f() {return new B;}
};
【2】析构函数的重写:(基类与派生类析构函数的名字不同)
  • 如果 基类的析构函数为虚函数 ,此时派生类析构函数只要定义, 无论是否加virtual关键字
    都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。 虽然函数名不相同【~Person() 】 【~Student() 】,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成 destructor
class Person {
public:                  //基类的析构函数为虚函数
 virtual ~Person() {cout << "~Person()" << endl;}
};

class Student : public Person {
public:
  //~Student() { cout << "~Student()" << endl; 不加virtual也行
 virtual ~Student() { cout << "~Student()" << endl; }
};
// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,
//才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{
 Person* p1 = new Person;
 Person* p2 = new Student;
 delete p1;
 delete p2;
 return 0;
}

三.【override】【final】关键字——帮助用户检测是否重写(C++11)

  • 从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数
    名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有
    得到预期结果才来debug会得不偿失,因此:C++11从两个角度提供了 override final 两个关键字,可以帮
    助用户检测是否重写。
  • final:表示虚函数不能被重写,被重写即报错
  • override:检查虚函数是否重写了别的虚函数,重写了即报错

【1】 final:表示虚函数不能被重写,被重写即报错

class Car
{
public:
 virtual void Drive() final {}
};
class Benz :public Car
{
public:
 virtual void Drive() {cout << "Benz-舒适" << endl;}
};

【2】override:检查虚函数是否重写了别的虚函数,重写了即报错

class Car{
public:
 virtual void Drive(){}
};
class Benz :public Car {
public:
 virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

四. 多态的具体应用:抽象类(接口类)(纯虚函数类)

1)利用 [ 只有重写纯虚函数 派生类才能实例化出对象 ] 性质

  • 在虚函数的后面写上 =0 ,则这个函数为 纯虚函数 包含纯虚函数的类 叫做 抽象类(也叫接口类) [ 抽象类不能实例化出对象 ]&[ 派生类继承后也不能实例化出对象 ] 只有 [ 重写纯虚函数 ] ,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
class Car
{
public:
virtual void Drive() = 0;         //在虚函数的后面写上 =0,这个函数为 纯虚函数
};

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();
}

2)实现继承与接口继承

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

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

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

相关文章

Codeforces Round 908 (Div. 2)视频详解

Educational Codeforces Round 157 &#xff08;A--D&#xff09;视频详解 视频链接A题代码B题代码C题代码D题代码 视频链接 Codeforces Round 908 (Div. 2)视频详解 A题代码 #include<bits/stdc.h> #define endl \n #define deb(x) cout << #x << "…

mac M2 anaconda 解决装不了python3.7

今天发现一个很奇怪的问题 但是我一换成 conda create -n DCA python3.8.12就是成功的 这个就很奇怪, 解决如下 https://towardsdatascience.com/how-to-manage-conda-environments-on-an-apple-silicon-m1-mac-1e29cb3bad12 998 conda search pythonconda search python …

C++之函数中实现类与调用总结(二百五十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

SpringMVC使用AOP监听方法推送数据

导入aop的maven依赖 <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.6.12</version> </dependency>创建一个spring的XML文件编写aop配置 <?xml version"1.0" …

基于springboot的二次元商品销售网站的设计与开发

大家好我是玥沐春风&#xff0c;今天分享一个基于springboot的二次元商品销售网站的设计与开发&#xff0c;项目源码以及部署相关请联系我&#xff0c;文末附上联系信息 。 开发工具及技术 2.3.1 Spring Boot框架 SpringBoot是一个全新的开源的轻量级框架。简化了Spring应用的…

Linux中的权限

目录 一、shell命令以及运行原理 二、Linux权限的概念 三、权限的八进制表示 四、修改文件的拥有者和所属组 五、权限常见的问题 1、目录的权限 2、umask 3、粘滞位 一、shell命令以及运行原理 首先&#xff0c;我们先来看看这个问题&#xff1a;我们使用命令是直接操…

electron安装报错:Electron failed to install correctly...解决方案

问题描述&#xff1a; 按照官方文档在yarn dev时报错&#xff1a; 一般遇到Electron failed to install correctly&#xff0c;please delete node_moules/electron and try installing again这种错误时&#xff0c;就是electron本体没有下载成功 解决方案&#xff1a; 1、…

GaN HEMT 电容的分析建模,包括寄生元件

标题&#xff1a;Analytical Modeling of Capacitances for GaN HEMTs, Including Parasitic Components 来源&#xff1a;IEEE TRANSACTIONS ON ELECTRON DEVICES&#xff08;14年&#xff09; 摘要&#xff1a;本文提出了一种基于表面势的终端电荷和电容模型&#xff0c;包…

Android 10.0 系统默认打开OEM解锁开关功能实现

1.前言 在10.0的系统定制中,在9.0系统以后为了设备的安装,系统开始启用oem机制,所以在adb push文件就需要先oem解锁,然后才可以 进行相关操作,所以就需要默认打开oem解锁的开关,来方便oem解锁功能的实现 如图: 2.系统默认打开OEM解锁开关功能实现的核心类 packages\ap…

【计算机网络笔记】网络层服务模型——虚电路网络

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

【C++数据结构】线性表的本质和概念

文章目录 前言一、线性表的定义1.1 线性表的定义1.2 线性表的表现形式1.3 线性表 ( List ) 的抽象定义1.4 线性表的性质1.5 简单解释 二、生活中的线性表三、抽象实现线性表List3.1 线性表的本质和操作3.2 抽象实现 总结 前言 在计算机科学与数据结构领域&#xff0c;线性表是…

使用comicai绘制漫画

在bard中输入提示语&#xff1a; 再写一个关于校园爱情的漫画脚本&#xff0c;里面的角色要求都是人类&#xff0c;没有动物&#xff0c;简短&#xff0c;用英文 填写漫画标题和作者&#xff1a; 将bard生成的脚本如何框中&#xff1a; 选择并生成角色形象&#xff08;通过提示…

Docsify 和 Hugo 之间的选型

对文档的编译&#xff0c;目前的发布方案是越来越注重 MD 的编辑和发布。 针对其他 Wiki 的选择&#xff0c;MD 文件的编辑通常会保留修改记录&#xff0c;同时不依赖中央数据库和其他类型的 Web 应用服务。 随着各大云平台的支持&#xff0c;包括 GitHub Page 和 Google 的 …

时序预测 | MATLAB实现WOA-CNN-LSTM-Attention时间序列预测(SE注意力机制)

时序预测 | MATLAB实现WOA-CNN-LSTM-Attention时间序列预测&#xff08;SE注意力机制&#xff09; 目录 时序预测 | MATLAB实现WOA-CNN-LSTM-Attention时间序列预测&#xff08;SE注意力机制&#xff09;预测效果基本描述模型描述程序设计参考资料 预测效果 基本描述 1.MATLAB实…

AI:72-基于深度学习的火灾检测

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

【LeetCode刷题笔记】栈和队列

456. 132 模式 解题思路: 1. 单调递减栈,栈中存放的值当作 k 值,从右往左遍历数组,对于遇到的每一个元素 j 找最大的 k,遇到 nums[j] > 栈顶</

mysql explain type 枚举

explain 查看 sql 查询是否走索引。 其中 type 的枚举如下 类型说明system表只有一行&#xff08;系统表&#xff09;&#xff0c;这是 const 类型的特例const单表中的某个固定的值eq_ref使用唯一索引等值查找一个行ref使用非唯一索引查找所有匹配某个单个值的行fulltext使用…

如何摆脱自卑心理,自我提升和自我接纳是关键

自卑心理主要是由于缺乏对自己的客观评价&#xff0c;常常自我否定&#xff0c;缺乏自信心&#xff0c;不敢拿主意做决定&#xff0c;他们性格敏感&#xff0c;缺乏勇气&#xff0c;不敢发表自己的意见&#xff0c;总是将错误归结为自己不够好&#xff0c;不够努力等等。 适度…

多语言翻译软件 Mate Translate mac中文版特色功能

Mate Translate for Mac是一款多语言翻译软件&#xff0c;Mate Translate mac可以帮你翻译超过100种语言的单词和短语&#xff0c;使用文本到语音转换&#xff0c;并浏览历史上已经完成的翻译。你还可以使用Control S在弹出窗口中快速交换语言。 Mate Translate Mac版特色功能…

浅谈无线测温产品在马来西亚某配电项目的应用

摘要&#xff1a;配电系统是由多种配电设备和配电设施所组成的变换电压和直接向终端用户分配电能的一个电力网络系统。由于配电系统作为电力系统的一个环节直接面向终端用户&#xff0c;它的完善与否直接关系着广大用户的用电可靠性和用电质量&#xff0c;因而在电力系统中具有…