C++ 多态 纯干货讲解 复制可调试(1)

💯 博客内容:多态

😀 作  者:陈大大陈

🚀 个人简介:一个正在努力学技术的准C++后端工程师,专注基础和实战分享 ,欢迎私信!

💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 😘 😘

目录

虚函数继承的条件

普通调用

多态调用

例:

易错练习题

override 和 final

纯虚函数 

多态的本质

为什么对象不能实现多态?


 

虚函数继承的条件

1.虚函数重写

2.必须是父类的引用或者指针来调用 

普通调用

调用函数的类型是谁,就调用哪个类型的函数

多态调用

调用指针或者引用指向的对象,指向父类就调用父类,指向子类就调用子类。 

例:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:virtual void BuyTickets(){cout << "全价买票" << endl;}
};
class Student :public Person
{virtual void BuyTickets(){cout << "半价买票" << endl;}
};
void Func(Person& p)
{p.BuyTickets();
}
int main()
{Student st;Func(st);Person p;Func(p);}

 上面的代码要是把引用去了,就是两个全价买票,因为普通调用只看参数类型。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:virtual void BuyTickets(){cout << "全价买票" << endl;}~Person(){cout << "~Person" << endl;}
};
class Student :public Person
{
public:virtual void BuyTickets(){cout << "半价买票" << endl;}~Student(){cout << "~Student" << endl;}
};
void Func(Person& p)
{p.BuyTickets();
}
int main()
{Student st;Func(st);Person p;Func(p);}

一般情况下,析构没有问题。

#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class Person
{
public:virtual void BuyTickets(){cout << "全价买票" << endl;}~Person(){cout << "~Person" << endl;}
};
class Student :public Person
{
public:virtual void BuyTickets(){cout << "半价买票" << endl;}~Student(){cout << "~Student" << endl;}
};
void Func(Person& p)
{p.BuyTickets();
}
int main()
{/*Student st;Func(st);Person p;Func(p);*/Person* ptr = new Person;delete ptr;ptr = new Student;delete ptr;}

 在new的情况下就会有问题,因为析构没有满足多态的条件,所以析构是普通调用。

加上virtual即可。

接下来来看一道易错练习题。

易错练习题

 答案是B,容易选成D,需要注意的是,虚函数继承声明,重写实现。

派生类可以不加virtual,所以B的func接口继承了A。

所以val的值是1。

之后B对象切片后传参调用A的test,A的test调用B的func。

如果调用的语句是,p->func(),就不构成多态,答案会是B->0。

override 和 final

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

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

2. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

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


 

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

纯虚函数 

 在虚函数的后面写上 =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(); }
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class base
{
public:virtual void func1(){cout << "func1()" << endl;}virtual void func2(){cout << "func2()" << endl;}void func3(){cout << "func3()" << endl;}
private:int _b = 1;char _ch;
};int main()
{cout << sizeof(base) << endl;base b;return 0;
}

虚函数里会存一个虚函数表指针来保存地址。

vfptr(virtual function pointer);

它是一个函数指针数组。 

父类和派生类的虚表指针不尽相同,重写了哪个虚函数,哪个虚表指针就会被覆盖。 

也就是说:

至此,我们明白了多态的本质

多态的本质

多态为什么能指向父类调父类,指向子类调子类?

虚函数的重写也叫虚函数的覆盖。

编译器首先在对象里找到虚函数表的地址。

查找虚函数表中存储的地址,是父类就调用父类,是子类就切割成父类对象。

是因为虚表指针会去寻找所调用的类的地址。 

为什么对象不能实现多态?

 因为指针和引用可以指向子类对象中切割出来的父类的一部分。

而对象的拷贝不会拷贝虚表指针。

如果拷贝虚表指针的话,父类和子类的虚函数以及析构函数极易混淆,造成报错。

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

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

相关文章

UI设计感蓝色商务数据后台网站模板源码

蓝色商务数据后台网站模板是一款适合网站模板下载。提示&#xff1a;本模板调用到谷歌字体库&#xff0c;可能会出现页面打开比较缓慢。 演示下载 qnziyw点cn/wysc/qdmb/20852点html

[概述] 获取点云数据的仪器

这里所说的获取点云的仪器指的是可以获取场景中物体距离信息的相关设备&#xff0c;下面分别从测距原理以及适用场景来进行介绍。 一、三角测距法 三角测距原理 就是利用三角形的几何关系来测量物体的距离。想象一下&#xff0c;你站在一个地方&#xff0c;你的朋友站在另一…

AI:49-基于深度学习的杂草识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…

【小白专用】微信小程序个人中心、我的界面(示例一)23.11.04

微信小程序使用button按钮实现个人中心、我的界面&#xff08;示例一&#xff09; 微信小程序个人中心、我的界面&#xff0c;使用button按钮实现界面布局&#xff0c;更好的将分享好友、获取头像等功能展现出来&#xff0c;更多示例界面&#xff0c;请前往我的主页哦。 1、js…

【腾讯云HAI域探秘】速通腾讯云HAI

速览HAI 产品简介 腾讯云高性能应用服务(Hyper Application lnventor&#xff0c;HA)&#xff0c;是一款面向 Al、科学计算的 GPU 应用服务产品&#xff0c;为开发者量身打造的澎湃算力平台。无需复杂配置&#xff0c;便可享受即开即用的GPU云服务体验。在 HA] 中&#xff0c;…

MySQL 表的增删查改(CRUD)

MySQL 表的增删查改(CRUD) 文章目录 MySQL 表的增删查改(CRUD)1. 新增(Create)2. 查询(Retrieve)2.1 全列查询2.2 指定列查询2.3 查询字段为表达式2.4 别名2.5 去重&#xff1a;DISTINCT2.6 排序&#xff1a;ORDER BY2.7 条件查询2.8 分页查询: LIMIT 3. 修改(Update)4. 删除(D…

【操作系统】多线程同步与互斥

文章目录 一. 实验目的二. 实验内容三. 实验步骤四. 实验结果五. 实验总结 一. 实验目的 &#xff08;1&#xff09;加强对进程同步和互斥的理解&#xff0c;学会使用信号量解决资源共享问题。 &#xff08;2&#xff09;熟悉Linux 进程同步原语。 &#xff08;3&#xff09;掌…

Java web(六):FilterListenerAJAX

文章目录 一、Filter1.1 基本介绍1.2 过滤器的执行流程1.3 拦截路径配置1.4 过滤器链1.5 案例 二、Listener三、AJAX3.1 快速入门3.2 Axios异步框架 四、 JSON4.1 JSON基础语法4.2 Fastjson 五、 案例JSONAxiosServlet Java web的三大组件&#xff1a;Servlet、Filter、Listene…

python工具三星路由器远程命令执行漏洞

无人扶我青云志&#xff0c;我自踏雪至山巅&#xff1b;​倘若命中无此运&#xff0c;孤身亦可登昆仑 python工具 漏洞证明&#xff1a; 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感谢。 免责声明&#xff1a;由于传播或利用此文所提供的…

Python实现自动化网页操作

1 准备 推荐使用Chrome浏览器 1.1 安装selenium程序包 激活虚拟环境&#xff0c;打开新的Terminal&#xff0c;输入以下代码&#xff1a; python -m pip install selenium 如下图所示&#xff0c;表示安装成功&#xff0c;版本为4.7.2 安装成功 关闭虚拟环境&#xff0c;打…

手动制作Docker容器镜像

文章目录 手动制作Docker容器镜像说明前期准备制作镜像1.启动一个centos系统的容器2.在centos容器中源码安装httpd服务3.基于已经安装好httpd服务的centos容器制作一个httpd镜像4.验证制作出来的镜像的功能5.上传至自己的docker镜像仓库&#xff08;可选&#xff09; 手动制作D…

Apache Doris (五十一): Doris数据缓存

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1.