【C++进阶学习】第四弹——多态——迈向C++更深处的关键一步

前言:


在前面我们已经学习了C++中继承的相关知识,已经体会到C++在与C语言的对比中的便捷性,但是有一些问题并没有被解决,比如继承中如何使不同的派生类公用基类的一个函数,这就需要多态的知识,而且,有一个很重要的点要知道,多态是以后找工作的时候经常经常被问到的一个知识

目录

一、多态的概念

二、多态的实现

2.1 多态的构成条件

2.2 override 和 final 关键字(C++11)

2.3 重载、覆盖(重写)、隐藏(重定义)的对比

三、抽象类

四、总结


一、多态的概念

C++中多态的概念通俗来讲就是多种形态,同样的东西,在不同场景下发挥着不同的作用;体现在代码上其实就是同一个虚函数,在不同的派生类中可能发挥着不同的作用,就比如一把雨伞既可以用来挡雨,也可以用来遮阳

二、多态的实现

2.1 多态的构成条件

在C++中,要实现多态,需要满足以下几个条件:

1、基类中必须包含至少一个虚函数: 虚函数是在基类中声明的,并在派生类中重写的函数。通过在函数声明前加上virtual关键字来声明虚函数。虚函数是实现动态多态的关键,因为它允许在运行时根据对象的实际类型来调用相应的函数。

2、通过基类指针或引用调用虚函数: 多态通常通过基类的指针或引用来实现。当使用基类指针或引用指向派生类对象时,调用虚函数将根据对象的实际类型(而不是指针或引用的类型)来决定调用哪个函数。

3、派生类必须重写(override)基类的虚函数: 派生类需要重写基类中的虚函数,以提供特定于派生类的实现。重写时,函数签名(包括返回类型、函数名和参数列表)必须与基类中的虚函数完全匹配。在C++11及以后的版本中,可以使用override关键字显式声明派生类中的函数是重写基类的虚函数,这有助于编译器检查是否正确重写了虚函数。

4、使用虚析构函数: 如果基类中使用了虚函数,通常建议也将析构函数声明为虚函数。这是因为当通过基类指针删除派生类对象时,如果析构函数不是虚函数,将只会调用基类的析构函数,而不会调用派生类的析构函数,这可能导致资源泄漏。

下面是一个简单的示例,展示了多态的构成条件:

#include <iostream>class Base {
public:virtual void show() {  // 虚函数std::cout << "Base show()" << std::endl;}virtual ~Base() {}  // 虚析构函数
};class Derived : public Base {
public:void show() override {  // 重写基类的虚函数std::cout << "Derived show()" << std::endl;}
};int main() {Base* ptr = new Derived();  // 基类指针指向派生类对象ptr->show();  // 调用派生类的show(),显示 "Derived show()"delete ptr;  // 正确调用析构函数return 0;
}

在这个例子中,Base类有一个虚函数show()和一个虚析构函数。Derived类重写了show()函数。在main()函数中,通过基类指针ptr调用show()函数,实际执行的是Derived类的show()函数,展示了动态多态的效果。同时,删除ptr时,会正确调用DerivedBase的析构函数。

2.2 override final 关键字(C++11)

通过上面的限制条件,我们已经可以看出要想构成多态还是不容易的,在我们平时写多态的时候,经常可能因为某些小差错而导致构建失败,而这种编译错误是不会指出来的,所以在C++11中提供了这两个关键字来帮助我们更容易的实现多态

override 关键字

override 关键字用于在派生类中明确地指示一个成员函数是重写了基类中的虚函数。这样做可以增加代码的可读性,并且能够帮助编译器检测错误,比如当试图重写一个基类中并不存在的虚函数时。

用法示例:

class Base {
public:virtual void display() const {// 基类实现}
};class Derived : public Base {
public:void display() const override { // 明确这是一个重写的函数// 派生类实现}
};

如果不使用 override,编译器仍然可以正确地识别出重写的函数,但使用 override 可以让意图更加明确,并且能够检测出一些错误。

final 关键字
final 关键字用于阻止类被进一步继承,或者阻止虚函数被进一步重写。

final 用于类时,表示该类不能被继承。
final 用于虚函数时,表示该函数不能被派生类重写。

用法示例:

class Base {
public:virtual void display() const final {// 基类实现}
};class Derived : public Base {// 下面的重写会失败,因为基类的 display 函数被标记为 final// void display() const override {//     // 派生类实现// }
};class FinalClass final { // 这个类不能被继承// ...
};// 下面的继承会失败,因为 FinalClass 被标记为 final
// class DerivedFromFinal : public FinalClass {
//     // ...
// }

使用 final 可以确保类的接口不会被意外地改变,这对于维护代码的稳定性和可预测性非常有帮助。

总结
override final C++11中用于控制虚函数行为的两个关键字。override 用于指示派生类中的成员函数是重写了基类的虚函数,而 final 用于阻止类被继承或虚函数被重写。这两个关键字都有助于提高代码的清晰性和安全性。

2.3 重载、覆盖(重写)、隐藏(重定义)的对比

三、抽象类

在 C++ 中,抽象类是一个不能直接实例化的类,它主要用于定义一组接口,要求其子类必须实现这些接口。

抽象类通常包含以下特点:

1、纯虚函数:抽象类中至少有一个或多个函数声明为 virtual 并且没有实现(即函数体为空,没有 = 0 后面的函数体),这些函数被称为纯虚函数(Pure Virtual Function)。例如:

class MyAbstractClass {
public:virtual void abstractMethod() = 0; // 纯虚函数
};

2、继承:子类可以继承抽象类,但是不能直接实例化抽象类。子类必须实现抽象类中所有纯虚函数,否则子类也将成为抽象类。例如:

class DerivedClass : public MyAbstractClass {
public:void abstractMethod() override; // 必须实现抽象方法
};

3、设计目的:抽象类通常用于为一组相关的类提供一个共同的行为框架,或者作为接口的定义,避免代码重复。

4、使用场景:抽象类常用于模式设计,如工厂模式、策略模式等,以及多态和模板编程中。

当你试图创建一个抽象类的对象时,编译器会报错,因为不能创建抽象类的实例。抽象类只有在将其中的纯虚函数重写之后才能实例化对象。抽象类主要用于定义接口,实际的业务逻辑通常由继承它的具体子类来实现。

四、总结

以上就是C++中多态的基本知识,总的来说并不是很难,但我们还没有将多态底层的原理——虚函数表的问题,这个与前面继承那个类似,都是很重要的知识点,这些我们放在后面统一讲解。

感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

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

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

相关文章

boost asio异步服务器(4)处理粘包问题tlv

粘包的产生 当客户端发送多个数据包给服务器时&#xff0c;服务器底层的tcp接收缓冲区收到的数据为粘连在一起的。这种情况的产生通常是服务器端处理数据的速率不如客户端的发送速率的情况。比如&#xff1a;客户端1s内连续发送了两个hello world&#xff01;,服务器过了2s才接…

正版软件 | Copywhiz 6:革新您的文件复制、备份与管理体验

在数字化时代&#xff0c;文件管理的效率直接影响到我们的生产力。Copywhiz 6 最新版本&#xff0c;带来了前所未有的文件处理能力&#xff0c;让复制、备份和组织文件变得轻而易举。 智能选择&#xff0c;只复制更新内容 Copywhiz 6 的智能选择功能&#xff0c;让您只需几次点…

学习js带有返回值的函数笔记

今天在写一个带有返回值的函数时遇到一个执行顺序的问题&#xff0c;查了半天资料才发现问题 js代码 function myFunction(a, b) {return a * b; }document.getElementById("myFunction").innerHTML myFunction(4, 4);html代码&#xff08;这是能正确运行出结果的…

【PA交易】BackTrader: 讨论下分析器和评测指标

前言 BackTrader的分析器主要使用的是analyzers模块&#xff0c;我们可以从Analyzers - Backtrader找到一个非常简单的示例。这个示例中使用方式很简单&#xff0c;其他分析器也可以通过如此简单封装方式进行装载。如果仅是复制粘贴官方教程&#xff0c;完全是制造互联网垃圾…

Netty学习(一)——基础组件

根据黑马程序员netty视频教程学习所做笔记。 笔记demo&#xff1a;https://gitee.com/jobim/netty_learn_demo.git 参考博客&#xff1a;https://blog.csdn.net/cl939974883/article/details/122550345 一、概述 1.1 什么是Netty Netty is an asynchronous event-driven netw…

Redis-哨兵模式-主机宕机-推选新主机的过程

文章目录 1、为哨兵模式准备配置文件2、启动哨兵3、主机6379宕机3.4、查看sentinel控制台日志3.5、查看6380主从信息 4、复活63794.1、再次查看sentinel控制台日志 1、为哨兵模式准备配置文件 [rootlocalhost redis]# ll 总用量 244 drwxr-xr-x. 2 root root 150 12月 6 2…

label studio数据标注平台的自动化标注使用

&#xff08;作者&#xff1a;陈玓玏&#xff09; 开源项目&#xff0c;欢迎star哦&#xff0c;https://github.com/tencentmusic/cube-studio 做图文音项目过程中&#xff0c;我们通常会需要进行数据标注。label studio是一个比较好上手的标注平台&#xff0c;可以直接搜…

如何关闭win10音量调节时 左上角出现的黑框

目录 1.谷歌浏览器&#xff1a; 2.edge浏览器&#xff1a; 3.没得办法的办法&#xff1a; 4.官方回复&#xff1a; 1.谷歌浏览器&#xff1a; 把这行地址chrome://flags/#hardware-media-key-handling 输入到chrome的地址栏里&#xff0c;回车&#xff0c;把黄色里的Hardwa…

突出显示列,重点内容一目了然!

老师在发布查询时&#xff0c;希望学生家长一眼就能看到重要的信息&#xff0c;应该如何设置&#xff1f; 易查分的新功能&#xff1a;突出显示列&#xff0c;就可以轻松实现&#xff01;老师可以个性化设置突出显示列的样式&#xff0c;包括颜色、字体大小、隐藏标题等&#x…

P2实验室装修标准都有哪些

P2实验室&#xff08;也称为生物安全二级实验室&#xff0c;BSL-2实验室&#xff09;的装修标准需要满足一系列的设计和施工要求&#xff0c;以确保实验室的安全性和功能性。因此&#xff0c;P2实验室装修标准不仅要满足一般实验室的要求&#xff0c;还需符合生物安全的特殊规定…

项目实战—OFD文件转换成图片

引言&#xff1a;项目需要预览OFD文件&#xff0c;但前端对OFD文件支持太差&#xff0c;因此将OFD文件直接转换成PNG格式、Base64编码的数据并返回给前端 依赖 <dependency><groupId>org.ofdrw</groupId><artifactId>ofdrw-converter</artifactId&…

餐厅点餐系统JAVA全栈开发(SSM框架+MYSQL)

代码仓库 GitHub - JJLi0427/Online_Order_SystemContribute to JJLi0427/Online_Order_System development by creating an account on GitHub.https://github.com/JJLi0427/Online_Order_System 项目介绍 餐厅点餐系统包含用户使用界面和功能实现&#xff0c;后台店员和管…

微信公众号错误码对应解决方案

微信公众号错误码对应解决方案 错误码&#xff1a; 40164 在获取 AccessToken 时报错&#xff1a; API 调用发生错误&#xff1a;{“errcode”:40164,“ErrorCodeValue”:40164,“errmsg”:“invalid ip 106.214.46.33, not in whitelist hint: [jPUF_08441512]”,“P2PData”…

C++初学者指南-2.输入和输出---文件输入和输出

C初学者指南-2.输入和输出—文件输入和输出 文章目录 C初学者指南-2.输入和输出---文件输入和输出1.写文本文件2.读文本文件3.打开关闭文件4.文件打开的模式 1.写文本文件 使用&#xff1a; std::ofstream&#xff08;输出文件流&#xff09; #include <fstream> // 文…

Scala 中的匿名函数

Scala 中的匿名函数 Scala 中的匿名函数是指没有指定函数名称的函数&#xff0c;通常用于简单的功能实现或者作为参数传递给其他函数。使用匿名函数可以简洁地表达代码逻辑&#xff0c;提高代码的可读性和简洁性。 在 Scala 中&#xff0c;可以使用 > 符号来定义匿名函数。下…

面试题--Zookeeper

1. Zookeeper 是什么(了解) Zookeeper 是一个 分布式协调服务 的开源框架, 主要用来解决分布式集群中应用系统 的一致性问题, 例如怎样避免同时操作同一数据造成脏读的问题. ZooKeeper 本质上是 一个分布式的小文件存储系统 . 提供基于类似于文件系统的目录 树方式的数据存…

React@16.x(39)路由v5.x(4)常见应用场景(1)- 受保护的页面

目录 1&#xff0c;实现2&#xff0c;知识点1&#xff0c;Route.children 和 Route.render2&#xff0c;保存跳转 login 之前的路由3&#xff0c;解构参数 现在有3个页面 Home 页面Login 页面Personal 页面&#xff08;受保护&#xff0c;未登录无法进入&#xff09; 1&#…

几种常见的方式可以引入CSS文件

1. <link> 标签 使用 <link> 标签将外部样式表引入HTML文档。 <head><link rel"stylesheet" href"styles.css"> </head>2. <style> 标签 在HTML文档中使用 <style> 标签定义内部样式。 <head><sty…

YOLOv8关键点pose训练自己的数据集

这里写自定义目录标题 YOLOv8关键点pose训练自己的数据集一、项目代码下载二、制作自己的关键点pose数据集2.1 标注(非常重要)2.1.1 标注软件2.1.2 标注注意事项a.多类别检测框b.单类别检测框2.2 格式转换(非常重要)2.3 数据集划分三、YOLOv8-pose训练关键点数据集3.1 训练…

通过frp实现内外网映射

frp介绍和使用方法可以参考官网:安装 | frp 1、准备两台服务器&#xff0c;一台内网服务器A&#xff0c;一台有公网ip的外网服务器B(47.12.13.15) 2、去官方仓库下载frp安装包&#xff1a;Releases fatedier/frp GitHub 下载包根据自己服务系统选择 ​ 3、先在外网服务器…