C++进阶:继承和多态
文章目录
- ❤️继承
- 🩷继承与友元
- 🧡继承和静态成员
- 💛菱形继承及菱形虚拟继承
- 💚继承和组合
- ❤️多态
- 🩷什么是多态?
- 🧡多态的定义以及实现
- 💛虚函数
- 💚虚函数的重写
- 💙override和final
- 🩵抽象类
- ❤️总结
上一个C++继承的文章讲到了继承中的默认成员函数。
本篇文章接着上次的继续讲解。
上一篇文章:C++进阶:继承
❤️继承
🩷继承与友元
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{
protected:int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;//cout << s._stuNum << endl;
}
例如上面这串代码,注释掉的那部分 就是错的,因为基类的友元函数无法访问到派生类保护或者私有成员。
🧡继承和静态成员
普通的成员变量在继承中,会重新生成一份在派生类中,但是静态成员还是会这样吗?首先对于普通成员变量来说,我们举个例子:
class Student;
class Person
{
public:string _name; // 姓名
};
class Student : public Person
{
public:int _stuNum; // 学号
};
int main()
{Student s;Person p;s._name = "li";p._name = "ll";cout << s._name << endl;cout << p._name << endl;return 0;
}
上面这个例子输出是:

很显然,这两个_name不是同一个_name,是两份_name,我们再来讨论静态成员的继承关系:

可以看见,如果将普通成员 变量改成静态成员变量之后 ,改变一个类的内容,另一个也会跟着改变,从中可以看出其实静态 成员变量只有一份,而且两个类中的静态成员变量是相同的。
💛菱形继承及菱形虚拟继承
什么是菱形继承?要了解菱形继承这个概念首先我们要了解什么是单继承什么是多继承?
单继承:一个子类继承一个父类的属性和方法
多继承:一个子类可以继承多个父类的属性和方法
什么是菱形继承呢?

类似于下图的关系就是菱形继承,菱形继承有什么特点呢?菱形继承继承了B但是B继承了A,C也同理,所以在D中有两份A,所以这里就导致了一些问题。二义性和冗余性
什么是二义性?
由于多个基类中存在同名成员,导致编译器无法确定应该调用哪个基类的成员,从而引发的不确定性和编译错误。
什么是冗余性?
由于多继承导致的重复继承同一个基类,使得相同的数据成员或方法被继承多次,从而产生多余的拷贝和不必要的资源占用。这种冗余性会导致效率低下和维护困难。
由于D中有两份A所以这里产生了重复,所以存在冗余性,在调用的时候,具体不知道调用哪个,所以这里产生了二义性,如何解决二义性呢?我们可以指定类域,就可以解决二义性,但是冗余性用当前所学的知识是解决不了的,所以C++引入了一个概念,叫做虚继承。
虚继承是 C++ 中的一种继承机制,旨在解决多继承中的菱形继承(钻石继承)问题。菱形继承问题发生在一个类通过多个路径继承同一个基类,从而导致重复继承基类的成员。
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在B和C的继承A时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
语法书写:
class Person
{
public :string _name ; // 姓名
};
class Student : virtual public Person
{
protected :int _num ; //学号
};
class Teacher : virtual public Person
{
protected :int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :string _majorCourse ; // 主修课程
};
当继承将要产生过冗余信息的时候的类就用虚拟继承。

💚继承和组合
- public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
- 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
- 优先使用对象组合而不是继承。
- 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称
为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的
内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很
大的影响。派生类和基类间的依赖关系很强,耦合度高。 - 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象
来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复
用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。
组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
封装。 - 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有
些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
继承,可以用组合,就用组合。
什么是组合呢?
class A{}; class B { private: int _b; A _a; };
上面就是一个对象的组合
❤️多态
🩷什么是多态?
面向对象编程中的一个核心概念,它允许对象以多种形式出现,并通过相同的接口来调用不同的实现。在 C++ 中,多态通常通过函数重载、运算符重载和虚函数来实现。多态的主要类型包括编译时多态(静态多态)和运行时多态(动态多态)。
简单来说多态就是:就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
例如:每个动物都有叫声,我们将叫声写为一个函数,然后每个动物去继承这个 动物类,每个动物去调用叫声这个函数都会得到不同的结果,这就是多态。
多态分为两种:
- 静态多态:编译时多态是通过函数重载和运算符重载在编译时实现的。它允许同一个函数或运算符根据参数的不同执行不同的操作。
- 动态多态:运行时多态是通过虚函数和基类指针或引用在运行时实现的。它允许基类指针或引用调用派生类的重载方法。
本篇主要以动态多态为主。
🧡多态的定义以及实现
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了
Person。Person对象买票全价,Student对象买票半价。
在继承中要构成多态的必要条件(缺一不可):
- 虚函数重写
- 父类指针或者引用进行调用虚函数
💛虚函数
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
在Person类中形如BuyTicket一类的函数都是虚函数。
💚虚函数的重写
虚函数的重写的概念:
派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的
返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
函数BuyTicket()是一个虚函数,Student继承了Person类,BuyTicket()在Student中重写。
void Func(Person& p)
{ p.BuyTicket(); }
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);return 0;
}
函数Func是用父类的指针进行调用,很显然这构成了多态了,所以这里会根据实例化出来的对象的指向来调用具体函数Person调用的就是Person的BuyTicket,而Student调用的就是自己的BuyTicket。

虚函数重写的两个例外
- 协变(基类与派生类虚函数返回值类型不同)
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指
针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
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;}
};
- 析构函数的重写(基类与派生类析构函数的名字不同)
如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加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;
}
如果上面基类不写virtual则会报错,将析构函数写成虚函数保证正常的析构的过程。
💙override和final
这是两个关键字。
override:
用于表示一个虚函数(virtual function)在派生类中重写了基类中的虚函数。使用override关键字可以帮助编译器检测是否正确地重写了基类的虚函数。如果函数签名不匹配,编译器会报错,这有助于避免一些常见的编程错误。
override用于检查一个类中是否构成虚函数重写。
final:
- 用于类:阻止该类被进一步继承。
- 用于虚函数:阻止该虚函数在派生类中被重写。
class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() {cout << "Benz-舒适" << endl;}
};
如图所示,上图就表示Drive函数不能被重写。
重载、重写、隐藏对比:

🩵抽象类
要讲什么是抽象类,我们先讲什么是纯虚函数。
纯虚函数:在虚函数的后面写上 =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; }
};
注意:Drive不能实例化,如果在Benz中重写出Drive并且前面不写成虚函数,那么这个类也不能实例化。
❤️总结
当你掌握了C++中的继承和多态,你就迈出了成为真正优秀C++程序员的第一步。继承让你可以构建出层次化的类结构,通过代码复用和扩展实现高效的软件设计。多态则使得你的代码可以根据对象的实际类型动态地选择合适的函数实现,提升了程序的灵活性和可维护性。
通过继承和多态,你可以更加清晰地组织你的代码,将复杂的问题分解为简单的对象和行为,使得代码更易于理解和修改。同时,这些特性也是C++等面向对象编程语言的核心所在,掌握它们不仅仅是技术层面的提升,更是编程思维和设计能力的深化。
在你的学习之旅中,不断练习和实践是掌握继承和多态的关键。通过编写更复杂的项目和解决实际的编程问题,你将逐渐发现如何更好地利用这些特性来构建高效、可扩展的软件系统。
希望本文能为你在C++进阶之路上提供一些启发和帮助,祝愿你在编程的世界中不断进步,享受编程的乐趣!
相关文章:
C++进阶:继承和多态
文章目录 ❤️继承🩷继承与友元🧡继承和静态成员💛菱形继承及菱形虚拟继承💚继承和组合 ❤️多态🩷什么是多态?🧡多态的定义以及实现💛虚函数💚虚函数的重写💙…...
【八大排序】java版(上)(冒泡、快排、堆排、选择排序)
文章目录 一、冒泡排序(重点)思路代码 二、快排(面试重点)思路代码 三、堆排序(面试重点)思路代码 四、选择排序思路代码 一、冒泡排序(重点) 思路 前后两两数据进行比较,小的数据往前走,大的数据往后走,每一轮结束之后,最大的数…...
.Net Core 微服务之Consul(二)-集群搭建
引言: 集合上一期.Net Core 微服务之Consul(一)(.Net Core 微服务之Consul(一)-CSDN博客) 。 目录 一、 Consul集群搭建 1. 高可用 1.1 高可用性概念 1.2 高可用集群的基本原理 1.3 高可用集群的架构设计 1.3.1 主从复制架构 1.3.2 共享存储架构 1.3.3 负载均衡…...
C++ --> 类和对象(二)
前言 在前面简单的介绍了OOP,什么是类,在类中的this指针。接下来就深入理解类和对象。 默认成员函数 默认构造函数:用于在创建对象时初始化对象的成员变量。默认拷贝构造函数:用于使用已存在的对象来初始化新创建的对象。默认析构…...
利用宝塔安装一套linux开发环境
更新yum,并且更换阿里镜像源 删除yum文件 cd /etc/yum.repos.d/ 进入yum核心目录 ls sun.repo rm -rf * 删除之前配置的本地源 ls 配置阿里镜像源 wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo 配置扩展包 wge…...
VB 实例:掌握 Visual Basic 编程的精髓
VB 实例:掌握 Visual Basic 编程的精髓 引言 Visual Basic(简称VB)是一种由微软开发的高级编程语言,它结合了易于使用的界面和强大的编程功能,使得初学者和专业人士都能快速开发Windows桌面应用程序。本文将通过一系列实例,深入探讨VB编程的基础知识和高级技巧,帮助读…...
层次分析法:matlab代码实现
计算权重: 一、算术平均法 关于矩阵: 1、矩阵的输入写法 [ ; ; ]同行用空格或逗号隔开,不同行用分号间隔 2、矩阵求和 默认按列求和 asum(E) 等同于 asum(E,1) 得到行向量 按行求和 asum(E,2) 得到列向量 对整个矩阵求和 asum(E,"all&…...
07-7.5.3 处理冲突的方法
👋 Hi, I’m Beast Cheng 👀 I’m interested in photography, hiking, landscape… 🌱 I’m currently learning python, javascript, kotlin… 📫 How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以…...
几何距离与函数距离:解锁数据空间中的奥秘
几何距离:直观的空间度量 几何距离,顾名思义,是我们在几何学中熟悉的距离概念,如欧几里得距离、曼哈顿距离和切比雪夫距离等。这些距离度量直接反映了数据点在多维空间中的位置关系。 欧几里得距离:最为人熟知的几何距…...
LabVIEW的Actor Framework (AF) 结构介绍
LabVIEW的Actor Framework (AF) 是一种高级架构,用于开发并发、可扩展和模块化的应用程序。通过面向对象编程(OOP)和消息传递机制,AF结构实现了高效的任务管理和数据处理。其主要特点包括并发执行、动态可扩展性和强大的错误处理能…...
gitlab 搭建使用
1. 硬件要求 ##CPU 4 核心500用户 8 核心1000用户 ##内存 4 G内存500用户 8 G内存1000用户 2. 下载 链接 3. 安装依赖 yum -y install curl openssh-server postfix wget 4. 安装gitlab组件 yum -y localinstall gitlab-ce-15.9.3-ce.0.el7.x86_64.rpm 5. 修改配置文…...
探索JT808协议在车辆远程视频监控系统中的应用
一、部标JT808协议概述 随着物联网技术的迅猛发展,智能交通系统(ITS)已成为现代交通领域的重要组成部分。其中,车辆远程监控与管理技术作为ITS的核心技术之一,对于提升交通管理效率、保障道路安全具有重要意义。 JT8…...
视频使用操作说明书-T80005系列视频编码器如何对接海康NVR硬盘录像机,包括T80005系列高清HDMI编码器、4K超高清HDMI编码器
视频使用操作说明书-T80005系列视频编码器如何对接海康NVR硬盘录像机,包括T80005系列高清HDMI编码器、4K超高清HDMI编码器。 视频使用操作说明书-T80005系列视频编码器如何对接海康NVR硬盘录像机,包括T80005系列高清HDMI编码器、4K超高清HDMI编码器 同三…...
keep-alive缓存组件
keep-alive缓存组件是Vue.js中的一个特殊组件,主要用于缓存内部组件的数据状态,以提高应用的性能和用户体验。以下是关于keep-alive缓存组件的详细解析: 一、作用 缓存组件状态:当组件在<keep-alive>内部切换时࿰…...
Linux上如何安装ffmpeg视频处理软件
在Linux上安装ffmpeg需要以下步骤: 更新系统 在开始安装之前,首先需要更新系统以获取最新的软件包列表和版本。在终端中执行以下命令: sudo apt update sudo apt upgrade安装依赖库 ffmpeg依赖于一些库和工具,需要先安装它们。在…...
element如何实现自定义表头?
有时候我们需要实现自定义表头,例如表头里加按钮啥的,这时候就需要用到自定义表头,但是官方对自定义表头的使用写的还是比较简单,今天就来详细说说 在需要使用自定义表头的表头上使用:render-header来启用自定义表头: <el-table-column :render-header="button&…...
OTP防重放攻击
OTP本意是一次性口令,比如邮箱验证码,短信验证码,或者根据totp或者hotp生成的默认30秒一变的6位数字。 不过开发者要注意,必须要在验证成功后失效那个验证码,不然就会导致重放攻击。 对于邮箱验证码,服务器…...
Oracle数据库加密与安全
Wallet简介: Oracle Wallet(即内部加密技术TDE( Transparent DataEncryption) TDE是 Oracle10gR2中推出的一个新功能,使用时要保证Oracle版本是在10gR2或者以上 Wallet配置: 1.创建一个新目录,并指定为Wallet目录 /home/oracle…...
【YOLO格式的数据标签,目标检测】
标签为 YOLO 格式,每幅图像一个 *.txt 文件(如果图像中没有对象,则不需要 *.txt 文件)。*.txt 文件规格如下: 每个对象一行 每一行都是 class x_center y_center width height 格式。 边框坐标必须是 归一化的 xywh 格式&#x…...
Memcached内存碎片清理术:优化缓存性能的策略
标题:Memcached内存碎片清理术:优化缓存性能的策略 内存碎片是Memcached在长期运行过程中常见的问题,它会降低缓存效率并影响性能。作为高效的分布式内存缓存系统,Memcached提供了多种内存碎片整理策略。本文将详细介绍这些策略&…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
《基于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…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
