C++之多态的深度剖析
目录
前言
1.多态的概念
2.多态的定义及实现
2.1多态的构成条件
2.1.1重要条件
2.1.2 虚函数
2.1.3 虚函数的重写/覆盖
2.1.4 选择题
2.1.5 虚函数其他知识
协变(了解)
析构函数的重写
override 和 final关键字
3. 重载,重写,隐藏的对比
4.纯虚函数和抽象类
结束语
前言
在前面我们对C++的封装,继承等特性都有了了解和学习,接下来我们将对C++的第三大特性-多态进行认识和掌握。内容分为来两大部分,第一个是对多态的认识和运用,第二大部分是对多态原理的了解和扩展。
1.多态的概念
多态(Polymorphism)是面向对象编程(OOP)中的一个核心概念,它指的是同一个行为具有多个不同表现形式或形态的能力。在编程中,多态通常通过继承(inheritance)和接(interfaces来实现。
以下是多态的几个主要方面:
-
编译时多态(静态多态):这是在编译时确定的多态性,通常通过函数重载(function overloading)和模板(templates)来实现。编译器根据函数的参数类型或数量来决定调用哪个函数。
-
运行时多态(动态多态):这是在程序运行时确定的多态性,主要通过虚函数(virtual functions)和继承来实现。在运行时,根据对象的实际类型来调用相应的成员函数。
多态的关键特性包括:
- 继承:子类继承父类的属性和行为,可以对这些行为进行重写(override)。
- 虚函数:在基类中声明为虚的成员函数,可以在派生类中被重写,使得通过基类指针或引用调用函数时,能够根据对象的实际类型来调用相应的函数版本。
- 虚函数表:用于实现运行时多态的数据结构,它存储了虚函数的地址,使得程序能够在运行时确定调用哪个函数。
- 向上转型:将派生类对象的引用或指针转换为基类类型的引用或指针,这是多态实现的基础。
2.多态的定义及实现
2.1多态的构成条件
2.1.1重要条件
2.1.2 虚函数
class Person {
public:virtual void BuyTicket() {cout << "买票全额" << endl;}
}; 2.1.3 虚函数的重写/覆盖
#define _CRT_SECURE_NO_WARNINGS
#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();
}
//指针调用
void func1(Person* p) {p->BuyTicket();// 这⾥可以看到虽然都是Person指针Ptr在调⽤BuyTicket// 但是跟ptr没关系,⽽是由ptr指向的对象决定的。
}
int main() {Person p1;Student s1;Person* p2 = new Person();Student* s2 = new Student();func(p1);func(s1);p1.BuyTicket();s1.BuyTicket();func1(&p1);func1(&s1);p2->BuyTicket();s2->BuyTicket();return 0;
}

void func(Student& p) {
p.BuyTicket();
}
//指针调用
void func1(Student* p) {
p->BuyTicket();
}
如果改成Student,就会出问题,就不是多态了,也就不能传Person对象了。
#include <iostream>
using namespace std;class Pet {
public:virtual void eat() const{cout << "Eat food" << endl;}
};
class Dog : public Pet{
public:virtual void eat() const {cout << "Dog eats meat!" << endl;}
};
class Cat :public Pet {
public:virtual void eat()const {cout << "Cat eats fish!" << endl;}
};
void func(const Pet& p) {p.eat();
}
int main() {Pet p;Dog g;Cat c;func(p);func(g);func(c);return 0;
}
上述是宠物的一个多态实现。
这里我们测试一下,基类函数不加virtual会怎样,
class Pet {
public:
void eat() const{
cout << "Eat food" << endl;
}
};

我们会发现多态效果没有实现,所以一定要加上virtual.
2.1.4 选择题
下面程序输出结果是什么?(B)
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; }
B* p = new B;创建了一个B类型的对象,并通过基类指针p指向它。p->test();调用了A类的test方法(因为B类没有重写test方法)。- 在
A类的test方法中,func(val)被调用,没有指定val的值,因此它使用A类func方法的默认参数1。 - 由于
func是虚函数,并且p指向一个B类型的对象,所以B类的func方法被调用,接收到的参数是1。
2.1.5 虚函数其他知识
协变(了解)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;class A {};
class B : public A {};
class Person {
public:virtual A* BuyTicket(){cout << "买票-全价" << endl;return nullptr;}
};
class Student : public Person {
public:virtual B* BuyTicket(){cout << "买票-打折" << endl;return nullptr;}
};
void Func(Person* ptr)
{ptr->BuyTicket();
}
int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}
析构函数的重写
故在C++中,当一个基类的析构函数被声明为虚函数时,它确保了当通过基类指针或引用删除派生类对象时,会调用正确的析构函数,即派生类的析构函数,然后再调用基类的析构函数。这是因为虚析构函数允许动态绑定,确保了派生类对象被正确地销毁。
#include <iostream>
using namespace std;
class A {
public:virtual ~A() {cout << "delete A" << endl;}
};
class B :public A {public:~B() {cout << "~B()->delete:" << _p << endl;delete _p;}
protected:int* _p = new int[10];
};
int main() {A* a = new A;A* b = new B;delete a;delete b;return 0;
}

当我们不把基类析构函数设置成virtual时, 会发现没有调用B的析构,该释放的资源没有释放掉。
public:~A() {cout << "delete A" << endl;}
};

故基类的析构函数我们要设置成虚函数。
override 和 final关键字
override 关键字用于明确指出一个成员函数旨在重写(覆盖)其基类中的一个虚函数。如果该函数没有正确地重写基类中的任何虚函数,编译器将报错。这有助于避免因拼写错误或参数列表不匹配而意外地没有重写虚函数的情况。
class Car {public:virtual void Dirve(){}};class Benz :public Car {public:virtual void Drive() override { cout << "Benz-舒适" << endl; }};
比如上面这个例子,函数名写错了,重写失败,编译报错。

final 关键字用于防止类被进一步派生,或者防止虚函数被重写。当应用于类时,它表示这个类不能被继承。当应用于虚函数时,它表示这个虚函数不能在派生类中被重写。
class Car {
public:virtual void Dirve() final{}
};
class Benz :public Car {
public:virtual void Dirve(){ cout << "Benz-舒适" << endl; }
};

class Base final { // 不能从这个类派生其他类
public:
virtual void doSomething() const final {} // 这个虚函数不能被重写
};
// 下面的类声明会导致编译错误,因为 Base 是 final 的
// class Derived : public Base {};
// 下面的函数声明也会导致编译错误,因为 doSomething 是 final 的
// class Derived : public Base {
// public:
// void doSomething() const override {} // 错误:不能重写 final 函数
// };
使用 final 关键字可以确保类或虚函数的行为不会被意外的继承或重写改变,这对于设计那些不打算被扩展的类或函数非常有用。
3. 重载,重写,隐藏的对比
重载(Overloading)
- 定义:在同一作用域内,可以定义多个同名函数,只要它们的参数列表(参数的数量、类型或顺序)不同。
- 特点:
- 发生在同一类中。
- 参数列表必须不同。
- 返回类型可以不同,但不是区分重载的主要因素。
重写(Overriding)
- 定义:在派生类中提供一个与基类中虚函数同名、参数列表和返回类型相同的函数,以实现多态。
- 特点:
- 发生在基类和派生类之间。
- 参数列表和返回类型必须相同。
- 基类函数必须是虚函数。
- 使用
override关键字可以明确指出重写意图。
隐藏(Hiding)
- 定义:在派生类中定义一个与基类中成员(非虚函数或非静态成员变量)同名的成员,导致基类中的同名成员在派生类中不可见。
- 特点:
- 发生在基类和派生类之间。
- 可以是函数或变量。
- 如果是函数,参数列表不必相同。
- 如果派生类中的成员与基类中的成员具有相同的名称,但不同的参数列表,则基类成员被隐藏,而不是重载或重写。
4.纯虚函数和抽象类
#include <iostream>
using namespace std;
class Car {
public:virtual void Drive() = 0;};
class Benchi :public Car {
public:virtual void Drive() {cout << "Benchi-舒适" << endl;}
};class Baoma :public Car {
public:virtual void Drive() {cout << "Baoma-上手" << endl;}
};
int main() {Car car;Car* b = new Benchi();b->Drive();Car* m = new Baoma();m->Drive();return 0;
} 
这里Car是抽象类,所以无法实例化对象。
结束语
本期内容就到此结束了,内容有点多,下节我们将对多态的原理进行补充讲解。
最后感谢各位友友的支持!!!
相关文章:
C++之多态的深度剖析
目录 前言 1.多态的概念 2.多态的定义及实现 2.1多态的构成条件 2.1.1重要条件 2.1.2 虚函数 2.1.3 虚函数的重写/覆盖 2.1.4 选择题 2.1.5 虚函数其他知识 协变(了解) 析构函数的重写 override 和 final关键字 3. 重载,重写&…...
Microsoft Office PowerPoint制作科研论文用图
Microsoft Office PowerPoint制作科研论文用图 1. 获取高清图片2. 导入PPT3. 另存为“增强型windows元文件”emf格式4. 画图剪裁 1. 获取高清图片 这里指通过绘图软件画分辨率高的图片,我一般使用python画dpi600的图片。 2. 导入PPT 新建一个PPT(注意&a…...
go语言进阶之并发基础
并发 什么是并发,也就是我们常说的多线程,多个程序同时执行。 并发的基础 线程和进程 进程 进程是操作系统中一个重要的概念,指的是一个正在运行的程序的实例。它包含程序代码、当前活动的状态、变量、程序计数器和内存等资源。进程是系…...
po、dto、vo的使用场景
现在项目中有两类模型类:DTO数据传输对象、PO持久化对象,DTO用于接口层向业务层之间传输数据,PO用于业务层与持久层之间传输数据,有些项目还会设置VO对象,VO对象用在前端与接口层之间传输数据,如下图&#…...
聊一聊Elasticsearch的一些基本信息
一、Elasticsearch是什么 Elasticsearch简称ES,是一款分布式搜索引擎。它是在Apache Lucene基础之上采用Java语言开发的。 Elasticsearch的官方网站对它的解释是:Elasticsearch是一个分布式、RESTful的搜索和数据分析引擎。 通过上边的官方解释&#…...
Unity 两篇文章熟悉所有编辑器拓展关键类 (上)
本专栏基础资源来自唐老狮和siki学院,仅作学习交流使用,不作任何商业用途,吃水不忘打井人,谨遵教诲 编辑器扩展内容实在是太多太多了(本篇就有五千字) 所以分为两个篇章而且只用一些常用api举例,…...
Spring SPI、Solon SPI 有点儿像(Maven 与 Gradle)
一、什么是 SPI SPI 全名 Service Provider interface,翻译过来就是“服务提供接口”。基本效果是,申明一个接口,然后通过配置获取它的实现,进而实现动态扩展。 Java SPI 是 JDK 内置的一种动态加载扩展点的实现。 一般的业务代…...
合并排序算法(C语言版)
#include <stdio.h> void Copy(int *a, int *b, int left, int right) { int i; for(i0;i<right-left1;i) { a[ileft] b[i]; } } // 将 a[left,middle] 和 a[middle1,right]合并到 b[left, right]中 void Merge(int *a, int left, int midd…...
C++——输入一行文字,找出其中的大写字母、小写字母、空格数字以及其他字符各有多少。用指针或引用方法处理。
没注释的源代码 #include <iostream> using namespace std; int main() { char c; int ul0,ll0,sp0,di0,other0; cout<<"please input script c:"; while(cin.get(c)) { if(c\n) break; else if(c>A&&…...
【skywalking】maximum query complexity exceeded 3336 > 3000
问题 skywalking相关版本信息 jdk:17skywalking:10.1.0apache-skywalking-java-agent:9.3.0ElasticSearch : 8.8.2 问题描述 maximum query complexity exceeded 3336 > 3000 最大查询复杂度超过3336>3000 可能原因 查询条件过于复…...
开源一个开发的聊天应用与AI开发框架,集成 ChatGPT,支持私有部署的源码
大家好,我是一颗甜苞谷,今天分享一个开发的聊天应用与AI开发框架,集成 ChatGPT,支持私有部署的源码。 介绍 当前系统集成了ChatGPT的聊天应用,不仅提供了基本的即时通讯功能,还引入了先进的AI技术&#x…...
开发了一个成人学位英语助考微信小程序
微信小程序名称:石榴英语 全称:石榴英语真题助手 功能定位 北京成人学士学位英语辅助学习工具,包含记高频单词,高频词组,专项练习,模拟考试等功能。 开发背景 个人工作需要提高学习英文水平ÿ…...
LeetCode16:最接近的三数之和
原题地址:. - 力扣(LeetCode) 题目描述 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 示例 1…...
VisualStudio2022配置2D图形库SFML
文章目录 1. 下载安装SFML库2. 创建C项目并配置SFML配置include目录和库目录链接SFML库配置动态链接库 3. 测试 1. 下载安装SFML库 SFML(Simple and Fast Multimedia Library)C库,适合2D游戏和图形界面,提供了以下模块࿱…...
「Mac畅玩鸿蒙与硬件4」鸿蒙开发环境配置篇4 - DevEco Studio 高效使用技巧
本篇将进一步介绍如何在 DevEco Studio 中高效使用各种功能,通过掌握快捷键、代码补全、调试工具等,帮助开发者在鸿蒙应用开发中大幅提升工作效率。 关键词 DevEco Studio快捷键代码补全调试工具项目导航 一、快捷键与高效操作 快捷键是提升开发效率的…...
构建生产级的 RAG 系统
对 RAG 应用程序进行原型设计很容易,但要使其高性能、健壮且可扩展到大型知识语料库却很困难。 本指南包含各种提示和技巧,以提高 RAG 工作流程的性能。我们首先概述一些通用技术 - 它们按照简单到复杂的顺序进行排列。然后,我们将更深入地研…...
完全透彻了解一个asp.net core MVC项目模板2
这是《完全透彻了解一个asp.net core MVC项目模板》的第二篇,如果你直接进入了本篇博文而不知道上下文,请先阅读《完全透彻了解一个asp.net core MVC项目模板》的第一篇。 文章目录 一、补充几个问题1、有关导航链接和Tag Helper2、_ViewStart.cshtml与…...
uniapp 如何调用音频
uniapp调用音频 button点击 <view><button click"startPlay">开始播放</button></view>方法实现 startPlay() { const innerAudioContext uni.createInnerAudioContext();innerAudioContext.src /static/sounds/oqc.mp3;innerAudioContex…...
在Facebook运营中使用住宅IP的重要性
在当前社交媒体的浪潮中,Facebook作为全球最大的社交网络之一,吸引了数以亿计的用户。为了在这一平台上实现有效的运营和推广,越来越多的博主和营销人员正在寻求最佳的养号策略。其中,IP地址的选择显得尤为重要,尤其是…...
EJB项目如何升级SpringCloud
记录某金融机构老项目重构升级为微服务过程1 如何从EJB架构拆分微服务 这个非常有趣的过程,整个过程耗时大致接近半年时光,需要考虑到重构升级保留原来的业务线,而且还要考虑后续的维护成本,保留现有的数据库表结构,…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
