C++进阶之路---多态(一)

46f376db7bb34b068d1fb00b70fa40b7.jpeg

顾得泉:个人主页

个人专栏:《Linux操作系统》 《C++从入门到精通》  《LeedCode刷题》

键盘敲烂,年薪百万!


一、多态的概念

1.概念

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

feedeaf1ad534d4e9c6b3a5782963dc2.png

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

       再举个栗子: 最近为了争夺在线支付市场,支付宝年底经常会做诱人的扫红包-支付-给奖励金的活动。那么大家想想为什么有人扫的红包又大又新鲜8块、10块…,而有人扫的红包都是1毛,5毛…。其实这背后也是一个多态行为。支付宝首先会分析你的账户数据,比如你是新用户、比如你没有经常支付宝支付等等,那么你需要被鼓励使用支付宝,那么就你扫码金额 =random()%99;比如你经常使用支付宝支付或者支付宝账户中常年没钱,那么就不需要太鼓励你去使用支付宝,那么就你扫码金额 = random()%1;总结一下:同样是扫码动作,不同的用户扫得到的不一样的红包,这也是一种多态行为。

       ps:支付宝红包问题纯属瞎编,大家仅供娱乐。


二、多态的定义及实现

1.多态的构成条件

       多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。

那么在继承中要构成多态还有两个条件:

1.必须通过基类(父类)的指针或者引用调用虚函数
2.被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写(重写条件:函数名/参数/返回值相同 协变例外)

b1c15fe3bdd949c69f1fae16e0b64b62.png

2.虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数。

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

3.虚函数的重写

class Person
{
public:
    virtual void Buyticket()
    {
        cout << "票价--全价" << endl;
    }
};

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

       虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
       注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用。

void func(Person& p)
{
    p.Buyticket();
}
int main()
{
    Person p;
    Student s;

    func(p);
    func(s);

    return 0;
}

虚函数重写的两个例外:

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关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

class Person 
{
public:
     virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person 
{
public:
     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;
}

4.C++11 override 和 final

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

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

5.重载、覆盖、隐藏)的对比

3ac61f7477484b54999d000a1b39f35f.png


三、抽象类

1.概念

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

class Car
{
public:
    virtual void Drive() = 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.接口继承和实现继承

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


结语:C++关于多态的分享到这里就结束了,希望本篇文章的分享会对大家的学习带来些许帮助,如果大家有什么问题,欢迎大家在评论区留言~~~ 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IPSec NAT穿越原理

一、IPSec VPN在NAT场景中存在的问题 当某些组网中&#xff0c;有的分支连动态的公网IP地址也没有&#xff0c;只能由网络中的NAT设备进行地址转换&#xff0c;才能访问互联网&#xff0c;然而IPsec是用来保护报文不被修改的&#xff0c;而NAT需要修改报文的IP地址&#xff0c…

9、组合模式(结构性模式)

组合模式又叫部分整体模式&#xff0c;它创建了对象组的树形结构&#xff0c;将对象组合成树状结构&#xff0c;以一致的方式处理叶子对象以及组合对象&#xff0c;不以层次高低定义类&#xff0c;都是结点类 一、传统组合模式 举例&#xff0c;大学、学院、系&#xff0c;它们…

崇法致行法律知识竞赛活动方案

赛程安排分两天&#xff0c;两场进行。 第一天&#xff08;第一场&#xff09;&#xff08;初赛&#xff09; 共 16 个二级分行&#xff0c;每行三人&#xff0c;共16 个战队参赛。 第一轮——必答轮 在大屏幕上显示10个选择题&#xff08;5个单选、5个多选&#xff09;&…

docker安装ollama

拉取镜像 docker pull ollama/ollama 运行容器 &#xff08;挂载路径 D:\ollama 改成你自己喜欢的路径&#xff09; CPU only docker run -d -v D:\ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama Nvidia GPU&#xff08;没试过这个&#xff09; doc…

Vue脚手架

Vue脚手架 学习目标&#xff1a; 理解Node.js基本使用方法理解包资源管理器NPM的使用理解webpack的作用理解 vue-cli 脚手架 (重点)Element-UI 组件库 1.vue的格式&#xff1a;new Vue({//作用的视图el:"id选择器",//vue中的数据/*data:{key:value,key:value,...}…

判断链表回文

题目&#xff1a; //方法一&#xff0c;空间复杂度O(n) class Solution { public:bool isPalindrome(ListNode* head) {vector<int> nums; //放进数组后用双指针判断ListNode* cur head;while(cur){nums.emplace_back(cur->val);cur cur->next;}for(int i0…

Microsoft SQL Server 编写汉字转拼音函数

目录 应用场景 举例 函数实现 小结 应用场景 在搜索应用中&#xff0c;我们一般会提供一个搜索框&#xff0c;输入关健字&#xff0c;点击查询按钮以获取结果数据。大部分情况我们会提供模糊查询的形式以在一个或多个字段进行搜索以获取结果。这样可以简化用户的操作&…

高分1、2号卫星原始遥感影像数据

高分一号 高分一号卫高分一号卫星是中国高分辨率对地观测系统的首发星&#xff0c;突破了高空间分辨率、多光谱与宽覆盖相结合的光学遥感等关键技术&#xff0c;设计寿命5至8年。 高分辨率对地观测系统工程是《国家中长期科学和技术发展规划纲要(2006&#xff5e;2020年)》确定…

专题二 - 滑动窗口 - leetcode 1004. 最大连续1的个数 III | 中等难度

leetcode 1004. 最大连续1的个数 III leetcode 1004. 最大连续1的个数 III | 中等难度1. 题目详情1. 原题链接2. 基础框架 2. 解题思路1. 题目分析2. 算法原理3. 时间复杂度 3. 代码实现4. 知识与收获 leetcode 1004. 最大连续1的个数 III | 中等难度 1. 题目详情 给定一个二…

Linux之线程控制

目录 一、POSIX线程库 二、线程的创建 三、线程等待 四、线程终止 五、分离线程 六、线程ID&#xff1a;pthread_t 1、获取线程ID 2、pthread_t 七、线程局部存储&#xff1a;__thread 一、POSIX线程库 由于Linux下的线程并没有独立特有的结构&#xff0c;所以Linux并…

蓝牙系列十一:HCI层的数据格式

HCI层作为Host和Controlor链接的接口存在。以下是对HCI层的数据格式的解析。 1、参考&#xff1a;蓝牙协议core_v5.0.pdf 《Vol 2: Core System Package [BR/EDR Controller volume]》的“Part E: Host Controller Interface Functional Specification” 2. 协议栈框图 对于被…

Linux:kubernetes(k8s)pod的基础操作(6)

Linux&#xff1a;kubernetes&#xff08;k8s&#xff09;允许在任意节点使用kubectl命令&#xff08;5&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/136460090?spm1001.2014.3001.5501 我在前两张进行了基础环境的一系列搭建&#xff0c;现在就正…

NIFI从Oracle11G同步数据到Mysql_亲测可用_解决数据重复_数据跟源表不一致的问题---大数据之Nifi工作笔记0065

首先来看一下整体的流程: 可以看到了用到了上面的这些处理器,然后我们主要看看,这里之前 同步的时候,总是出现重复的数据,奇怪. 比如源表中只有166条数据,但是同步过去以后变成了11万条数据了. ${db.table.name:equals(table1):or(${db.table.name:equals(table2)})} 可以看…

职场成功的关键:积极主动,勇于担当

在职场中&#xff0c;每个人都渴望成功。然而&#xff0c;成功并非一蹴而就&#xff0c;而是需要我们在日常工作中不断积累、锻炼和提升。本文将为您揭示职场成功的关键因素&#xff0c;帮助您在职场道路上越走越远。 一、积极主动&#xff0c;主动承担责任 在职场中&#xff0…

吴恩达机器学习笔记十六 如何debug一个学习算法 模型评估 模型选择和训练 交叉验证测试集

如果算法预测出的结果不太好&#xff0c;可以考虑以下几个方面&#xff1a; 获得更多的训练样本 采用更少的特征 尝试获取更多的特征 增加多项式特征 增大或减小 λ 模型评估(evaluate model) 例如房价预测&#xff0c;用五个数据训练出的模型能很好的拟合这几个数据&am…

RocketMQ入门指南:从零开始学习分布式消息队列技术

RocketMQ 1. MQ介绍1.1 为什么要用MQ1.2 MQ的优点和缺点1.3 各种MQ产品的比较 2. RocketMQ快速入门2.1 准备工作2.1.1 下载RocketMQ2.2.2 环境要求 2.2 安装RocketMQ2.2.1 安装步骤2.2.2 目录介绍 2.3 启动RocketMQ2.4 测试RocketMQ2.4.1 发送消息2.4.2 接收消息 2.5 关闭Rocke…

【Python】科研代码学习:七 TrainingArguments,Trainer

【Python】科研代码学习&#xff1a;七 TrainingArguments&#xff0c;Trainer TrainingArguments重要的方法 Trainer重要的方法使用 Trainer 的简单例子 TrainingArguments HF官网API&#xff1a;Training 众所周知&#xff0c;推理是一个大头&#xff0c;训练是另一个大头 之…

【PyTorch实战演练】深入剖析MTCNN(多任务级联卷积神经网络)并使用30行代码实现人脸识别

文章目录 0. 前言1. 级联神经网络介绍2. MTCNN介绍2.1 MTCNN提出背景2.2 MTCNN结构 3. MTCNN PyTorch实战3.1 facenet_pytorch库中的MTCNN3.2 识别图像数据3.3 人脸识别3.4 关键点定位 0. 前言 按照国际惯例&#xff0c;首先声明&#xff1a;本文只是我自己学习的理解&#xff…

【Qt学习笔记】(二)--第一个程序“Hello World”(学习Qt中程序的运行、发布、编译过程)

声明&#xff1a;本人水平有限&#xff0c;博客可能存在部分错误的地方&#xff0c;请广大读者谅解并向本人反馈错误。    因为我个人对Qt也是有一些需求&#xff0c;所以开设本专栏进行学习&#xff0c;希望大家可以一起学习&#xff0c;共同进步。   这篇博客将从一个 He…

算法刷题Day1 | 704.二分查找、27.移除元素

目录 0 引言1 二分查找1.1 我的解题1.2 修改后1.3 总结 2 移除元素2.1 暴力求解2.2 双指针法&#xff08;快慢指针&#xff09; &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;算法专栏&#x1f4a5; 标题&#xff1a;代码随想录算法训练营第一天…