【C++】多态的使用详解

本篇要分享的内容是多态,以下为本篇目录。

目录

1.多态的概念

2. 多态的定义及实现

3.虚函数

4.C++11  override和final

4.1final关键字

4.2override关键字

 5.抽象类

5.1抽象类的概念

5.2接口继承和实现继承


1.多态的概念

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

比如旅行景点的成人票是全票,儿童半票,军人优先购票;

又比如拼多多的红包,新用户就会获得很多福利;而老用户的红包福利只有一点点。

像这样不同的身份可以产生不同的行为和结果,也是一种多态行为。

2. 多态的定义及实现

用一段简单的代码来认识多态

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

可以看到在多态中引入了一个新的概念: 虚函数(即在函数前加上virtual)。

并且在函数测试中使用父类创建了对象,并且调用函数,这时要注意我们在main函数中分别用父类和子类创建了对象,并调用了Func函数。

在之前的继承中我们知道,父类的引用即可传父类对象,也可以传子类对象

这时运行代码观察结果

 

可以看到这里调用了两个函数:
普通人买票全价,学生买票半价;

但是当我们将切片改为传值传参,而不是传引用传参时结果就会调用同一个函数

可以看到这里去掉了&符号

运行结果如下

可以看到调用到的时一个函数

那这就证明出形成多态的条件:

①.虚函数重写

②.父类的指针或者函数去调用虚函数

以上条件只要有一个不满足,就会变成普通调用,就会去看对象的类型

3.虚函数

在之前的继承中我们学到过虚继承的关键字也是virtual,但是和我们今天在多态中所学的虚函数是没有关系的,如同取地址符号&,和引用符号&,虽然符号相同,但是是有区别的。

要构建多态的第一个条件就是构成虚函数重写,那么构成虚函数重写也是有条件的:

继承关系中两个父子类关系的虚函数,函数名、参数、返回值,都要相同,才能完成虚函数的重写。

当然,virtual只能修饰成员函数,它的作用是修饰成员函数来构成多态,在类外使用当然是会直接报错的。

但是上述的三同(函数名、参数、返回值相同)又有一个例外,称之为协变:返回值可以不同,但是必须是父子类关系的指针或引用

如图

可以看到我们修改了函数的返回值是父子关系类的指针,结果也会构成多态。

但是在如上的这中情况下,子类又可以不加virtual

为什么要这样设计呢?

这里的函数需要重写,重写就是重复实现,也可以认为在父类中的虚函数,子类与父类同名的虚函数virtual也会继承下来。

以上的两种特殊用法是需要记忆的,是可以使用,但是在我们自己编写代码中最好还是使用三同来完成多态。

接下来则是析构函数,有如下场景

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

这里的分别在两个类中定义了两个析构函数,为方便观察我们输出他们。

再在main函数中分别定义两个指针,并且分别指向父类和子类并析构

运行结果如下 

 可以看到创建的两个指针指向的类不同,但是却同时调用了父类的析构函数,原因是在没有使用virtual修饰函数的情况下,所有的析构函数都被编译器命名为:destroy(),所以在两个不同的类中相当与重写了析构函数。

因为子类不能正常调用析构函数,所以有可能会造成内存泄漏。

 所以我们需要使用虚函数就可以使他们分别调用自己类中的析构函数

(子类调用析构函数是先子后父)。

4.C++11  override和final

在C++11中更新了两个关键字final和override;

4.1final关键字

1.final修饰类时,这个类不能被继承

2.final修饰虚函数时,这个虚函数不能被重写

用法如下

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

此处实在Car类中的虚函数后加上final {};

 可以看到使用final之后,这个虚函数就不能被重写了

并且这个函数必须是虚函数

 这里报错很明显,final不能修饰非虚函数。

在Car类后加上final之后这个类就不能被继承了 。

4.2override关键字

override用来修饰派生类的虚函数,用来检测是否完成重写。

以下是override的使用位置

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

如果没有完成重写就会报错

虚函数一定是要重写的,否则虚函数是没有意义的。

 5.抽象类

5.1抽象类的概念

上面我们提到过虚函数。在虚函数后面加上=0,则这个函数为纯虚函数

那么此时,包含整个纯虚函数的类叫做抽象类,也叫做接口类,抽象类不能实例化对象。 

可以看到上图的Car中有纯虚函数,Car就变成了抽象类,此时Car也就不能定义对象,但是可以定义指针。

 

上图中我们定义了新的类,并且继承了抽象类Car,可以看到此时新的类也不能实例化对象了; 

但是我们可以将纯虚函数重写,这样就可以实例化对象了

所以有以下使用场景

class Car
{
public:virtual void Drive() = 0;
};class Benz :public Car
{
public:void Drive(){cout << "Benz" << endl;}
};class BMW :public Car
{
public:virtual void Drive(){cout << "BMW-操控" << endl;}
};void func(Car* c)
{c->Drive();
}
int main()
{func(new Benz);func(new BMW);
/*Car* pBenz = new Benz;
pBenz->Drive();
Car* pBMW = new BMW;
pBMW->Drive();*/
}

在以上代码中我们发现在func函数中的参数定义了抽象类的指针对象c,利用c去调用类中重写的函数。

同时在main函数中调用函数,不难看出指向哪个子类就调用的是哪个子类重写的函数。

所以我们可以得出的结论是抽象类强制了子类去重写。

5.2接口继承和实现继承

普通函数的继承是一种实现继承

虚函数的继承是一种接口继承

用代码说明

首先是实现继承,我们在父类中简单写了一个输出函数,他会直接继承到子类中,也就是说子类可以直接对func函数直接进行调用,可以理解为一种函数的复用。

而虚函数的接口继承,相当于需要在子类中重写函数的实现,但是调用的参数,或者说是接口,还是父类的接口,所以我们才需要重写虚函数。

最后用一道小题来了解接口继承

class A{public:virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}virtual void test(){ func();}};class B : public A{public:void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }};int main(int argc ,char* argv[]){B*p = new B;p->test();return 0;}A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确

首先观察main函数中使用B类创建了指针p;

使用p去调用test,test在父类中,所以test中的this指针是指向A类

在test中又调用了func函数

因为func在A类中被定义为虚函数,并且在B类中重写,在加上虚函数为接口继承,

虚函数只重写函数体内容,而接口还是父类的接口

所以答案为:B->1

以上就是本篇要分享的关于多态的概念和简单实用,本人水平有限,尽管不遗余力但本篇的内容仍有不足,还请读者指正,感谢您的阅读。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

参考意义大。4+巨噬细胞相关生信思路,简单易复现。

今天给同学们分享一篇生信文章“Angiogenesis regulators S100A4, SPARC and SPP1 correlate with macrophage infiltration and are prognostic biomarkers in colon and rectal cancers”&#xff0c;这篇文章发表在Front Oncol期刊上&#xff0c;影响因子为4.7。 结果解读&a…

如何快速将钉钉员工信息同步到飞书

当企业内部在使用钉钉跟飞书时&#xff0c;那么当钉钉员工信息发生更改时&#xff0c;我们应该如何将信息快速同步到飞书上呢&#xff0c;接下来我们借助RestCloud AppLink平台进行演示。 第一步&#xff1a;获得钉钉以及飞书认证授权 钉钉授权 钉钉接入采用自建应用的方式&…

AC修炼计划(AtCoder Regular Contest 162)

传送门&#xff1a;AtCoder Regular Contest 162 - AtCoder A题签到 B - Insertion Sort 2 我们可以从头开始一个一个排序&#xff0c;把1通过操作放到第一个&#xff0c;把2通过操作放到第二个。。。以此类推。但会出现一种情况&#xff0c;如果我们所要排的数字在最后一位&…

Outlook邮件视图设置怎么修复

故障现象 Outlook邮箱显示不对 故障截图 故障原因 邮箱视图设置不对 解决方案 1、在Outlook上方工具栏找到视图按钮&#xff0c;以此选择视图→视图设置→列&#xff0c;打开选择的列 2、在视图→邮件预览里面&#xff0c;选择1行&#xff0c;在阅读格式选择靠右&#xff…

JZ22:链表中倒数第k个结点

JZ22&#xff1a;链表中倒数第k个结点 题目描述&#xff1a; 输入一个链表&#xff0c;输出该链表中倒数第k个结点。 示例1 输入&#xff1a; 1,{1,2,3,4,5} 返回值&#xff1a; {5} 分析&#xff1a; 快慢指针思想&#xff1a; 需要两个指针&#xff0c;快指针fast&…

贪吃蛇小游戏代码

框架区 package 结果;import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.util.ArrayList; import java.util.List; import java.util.Random;import javax.s…

解决Qt5.13.0无MySQL驱动问题

一、前言 由于Qt5.12.3是最后提供mysql数据库插件的版本&#xff0c;往后的版本需要自行编译对应的mysql数据库插件&#xff0c;官方安装包不再提供。使用高版本的Qt就需要自行编译mysql驱动。 若没有编译在QT中调用Qsqldatabase库连接mysql时&#xff0c;提示出现如下问题&a…

百度搜索智能化算力调控分配方法

作者 | 泰来 导读 随着近年深度学习技术的发展&#xff0c;搜索算法复杂度不断上升&#xff0c;算力供给需求出现了爆发式的增长。伴随着AI技术逐步走到深水区&#xff0c;算法红利在逐步消失&#xff0c;边际效应日益显著&#xff0c;算力效能的提升尤为重要&#xff0c;同时随…

俄罗斯方块小游戏

框架 package 框架;import java.awt.image.BufferedImage; import java.util.Objects;/*** author xiaoZhao* date 2022/5/7* describe* 小方块类* 方法&#xff1a; 左移、右移、下落*/ public class Cell {// 行private int row;// 列private int col;private BufferedIm…

一张图系列 - “position_embedding”

关于位置编码&#xff0c;我感觉应该我需要知道点啥&#xff1f; 0、需要知道什么知识&#xff1f; multi head atten 计算 复数的常识 1、embedding 是什么&#xff1f; position embedding常识、概念&#xff0c;没有会怎样&#xff1f; 交换token位置&#xff0c;没有P…

根据视频编码时间批量重命名视频文件

整理收藏的小视频的时候发现很多视频命名很随意&#xff0c;自己命名又太麻烦&#xff0c;看着乱糟糟的文件又心烦&#xff0c;所有写了这个程序&#xff0c;代码如下&#xff1a; import osfrom filetype import filetype from pymediainfo import MediaInfovideo_extension …

不敢信,30+岁的项目经理会是这样

大家好&#xff0c;我是老原。 你们知道&#xff0c;每个阶段的项目经理都是什么样的吗&#xff1f; 20多岁时&#xff0c;刚踏入项目管理的你可能是个什么都不懂的职场小白&#xff0c;或者只能在旁边打打下手&#xff1b; 到了30岁&#xff0c;经历了项目的人情冷暖&#…