当前位置: 首页 > news >正文

C++基础系列【14】继承与多态

博主介绍:程序喵大人

  • 35- 资深C/C++/Rust/Android/iOS客户端开发
  • 10年大厂工作经验
  • 嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手
  • 《C++20高级编程》《C++23高级编程》等多本书籍著译者
  • 更多原创精品文章,首发gzh,见文末
  • 👇👇记得订阅专栏,以防走丢👇👇
    😃C++基础系列专栏
    😃C语言基础系列

本文主要介绍下多态的概念。

继承与抽象类

多态是面向对象的核心知识点,在C++中意味着调用对象成员函数时,会根据对象的真实类型来执行不同的函数,从而产生不同的行为。

  • 比如同样是人,不同人的声音不相同。
  • 比如同样是公司,不同公司的经营业务也不同。

这就可以就多态来解释。

怎么实现多态,看这段代码,先定义一个People类:

class People {
public:
virtual void Speak() { std::cout << "People Speak \n"; }
};

注意这里面的函数使用了virtual修饰,用virtual修饰的函数表示虚函数,带虚函数的类可以称之为父类,有父类自然可以派生出子类,子类可以覆盖父类的行为。

这里再定义两个类,一个男人类,一个女人类

class MalePeople : public People {
public:
void Speak() { std::cout << "MalePeople Speak \n"; }
};class FemalePeople : public People {
public:
void Speak() { std::cout << "FemalePeople Speak \n"; }
};

MalePeopleFemalePeople使用了冒号,表示继承,冒号后面的public表示继承的权限。

所以上面的代码的含义是:

MalePeoplepublic权限继承了People,并覆盖父类PeopleSpeak行为。

FemalePeoplepublic权限继承了People,并覆盖父类PeopleSpeak行为。

再看一段使用多态的代码:

int main() {People *p1 = new People();People *p2 = static_cast<People *>(new MalePeople());People *p3 = static_cast<People *>(new FemalePeople());p1->Speak(); // People Speakp2->Speak(); // MalePeople Speakp3->Speak(); // FemalePeople Speakdelete p3;delete p2;delete p1;
}

p1、p2、p3都是People的实例,但是通过他们的实例调用相同的函数却产生了不同的行为,这就是多态

注意两点,想要实现上述的多态行为:

  • 父类相应的函数一定要使用virtual修饰
  • 一定要父类的指针或引用指向子类对象

继承权限

共有三种继承权限:

public继承

  • 父类中所有public成员在子类中为public属性
  • 父类中所有protected成员在子类中为protected属性
  • 父类中所有private成员在子类中不可访问

protected继承

  • 父类中所有public成员在子类中为protected属性
  • 父类中所有protected成员在子类中为protected属性
  • 父类中所有private成员在子类中不可访问

private继承

  • 父类中所有public成员在子类中为private属性
  • 父类中所有protected成员在子类中为private属性
  • 父类中所有private成员在子类中不可访问

大体可以理解为:

  • 父类成员在子类中的访问权限不会高于指定的继承权限。
  • 父类中的private成员在子类中使用不可访问。

然而平时开发过程中一般都会使用public继承,其他的继承方式很少。

纯虚函数

在C++中,还有个纯虚函数的概念,就是在virtual修饰的基础上加个=0,比如:

class People {
public:
virtual void Speak() = 0;
};

这里的Speak就是纯虚函数,含有纯虚函数的类叫抽象类,同时规定抽象类不允许被实例化,只能通过子类实例化,举例:

int main() {People *p1 = new People(); // compile errorPeople *p2 = static_cast<People *>(new MalePeople());People *p3 = static_cast<People *>(new FemalePeople());
}

多继承

就是子类继承了多个父类,比如一个男子篮球运动员,那就可以定义两个父类,一个MalePeople类,一个BasketballPlayer类,那如果想要定义男子篮球运动员类,可以定义一个MaleBasketballPlayer类,继承MalePeopleBaskeballPlayer,代码如下:

class MalePeople {
public:
void Speak() { std::cout << "MalePeople Speak \n"; }
};class BasketBallPlayer {
public:
void Play() { std::cout << "Play Basketball \n"; }
};class MaleBasketBallPlayer : public MalePeople, public BasketBallPlayer {};

和单继承方式差不多,只是用相同的语法在后面再派生多个即可。

虚继承

在这里插入图片描述

普通的继承就是非虚继承,如图, 非虚继承时,显然D会继承两次A,内部就会存储两份A的数据浪费空间,而且还有二义性,D调用A的方法时,由于有两个A,究竟时调用哪个A的方法呢,编译器也不知道,就会报错,所以有了虚继承,解决了空间浪费以及二义性问题。

在这里插入图片描述

在虚拟继承下,只有一个共享的基类子对象被继承,而无论该基类在派生层次中出现多少次。共享的基类子对象被称为虚基类。在虚继承下,基类子对象的复制及由此而引起的二义性都被消除了。

如何使用虚继承?

在继承的时候使用virtual关键字,代码如下:

struct Base {
virtual void Func() { printf("Base Func\n"); }
};struct BaseA : virtual public Base {
virtual void Func() { printf("BaseA Func\n"); }
};struct BaseB : virtual public Base {
virtual void Func() { printf("BaseB Func\n"); }
};struct Derive : public BaseB, public BaseA {
void Func() override { printf("Derive Func \n"); }
};

注意,为了易于观察,上面所有的父类都没有定义析构函数,正常父类的析构函数一定要设置成virtual

练习

  • 多态只有这一种方式吗?
  • 为什么一定要通过指针或引用方式才能达到多态的目的?
  • 为什么析构函数一定要设置成virtual?
  • 构造函数可以为虚函数吗?
  • 多态的原理是怎么样的?
  • 不同继承方式下,类对象的布局是什么结构?

码字不易,欢迎大家点赞关注评论,谢谢!


C++训练营

专为校招、社招3年工作经验的同学打造的1V1 C++训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得offer!训练营介绍

相关文章:

C++基础系列【14】继承与多态

博主介绍&#xff1a;程序喵大人 35- 资深C/C/Rust/Android/iOS客户端开发10年大厂工作经验嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手《C20高级编程》《C23高级编程》等多本书籍著译者更多原创精品文章&#xff0c;首发gzh&#xff0c;见文末&#x1f447;&#x1f…...

DeepSeek-R1 大模型本地部署指南

文章目录 一、系统要求硬件要求软件环境 二、部署流程1. 环境准备2. 模型获取3. 推理代码配置4. 启动推理服务 三、优化方案1. 显存优化技术2. 性能加速方案 四、部署验证健康检查脚本预期输出特征 五、常见问题解决1. CUDA内存不足2. 分词器警告处理3. 多GPU部署 六、安全合规…...

在conda环境下,安装Pytorch和CUDA

系统 : Ubuntu20.04 显卡&#xff1a;NVIDIA GTX1650 显卡驱动已经装好&#xff08;命令 nvidia-smi 查看显卡配置&#xff09; &#xff08;主要看一下第一行的参数&#xff0c;最大支持的CUDA版本为12.4 &#xff09; Aanconda 版本&#xff08;安装指南&#xff09;(似乎…...

Java里int和Integer的区别?

大家好&#xff0c;我是锋哥。今天分享关于【Java里int和Integer的区别&#xff1f;】面试题。希望对大家有帮助&#xff1b; Java里int和Integer的区别&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Java 中&#xff0c;int 和 Integer 都是用来表…...

【第13章:自监督学习与少样本学习—13.4 自监督学习与少样本学习的未来研究方向与挑战】

凌晨三点的实验室里,博士生小张盯着屏幕上的训练曲线——他设计的跨模态少样本学习模型在医疗影像诊断任务上突然出现了诡异的性能断崖。前一秒还在92%的准确率高位运行,下一秒就暴跌到47%。这个看似灾难性的现象,却意外揭开了自监督学习与少样本学习技术深藏的核心挑战… 一…...

【NLP】文本预处理

目录 一、文本处理的基本方法 1.1 分词 1.2 命名体实体识别 1.3 词性标注 二、文本张量的表示形式 2.1 one-hot编码 2.2 word2vec 模型 2.2.1 CBOW模式 2.2.2 skipgram模式 2.3 词嵌入word embedding 三、文本数据分析 3.1 标签数量分布 3.2 句子长度分布 3.3 词…...

deepseek r1从零搭建本地知识库10:嵌入模型和知识库建设

一、嵌入模型&#xff08;Embedding Model&#xff09;是什么&#xff1f; 1. 定义 嵌入模型是一种将文本、图像、音频等非结构化数据转化为**低维稠密向量&#xff08;Dense Vector&#xff09;**的算法模型&#xff0c;这些向量&#xff08;通常几百到几千维&#xff09;能够…...

Linux-文件IO

1.open函数 【1】基本概念和使用 #include <fcntl.h> int open(const char *pathname&#xff0c;int flags); int open(const char *pathname&#xff0c;int flags&#xff0c;mode_t mode); 功能: 打开或创建文件 参数: pathname //打开的文件名 f…...

3d pose 学习笔记2025

目录 champ nlf 3dpose 2025 55个关键点 推理代码: 要设置环境变量: 依赖项metrabs 渲染代码: tram4d 脚也不是特别好 GVHMR脚对不齐 推理代码: multiperson 2023年 genhmr还没开源: champ https://zhuanlan.zhihu.com/p/700326554 nlf 3dpose 2025 55个关键点…...

LC-随机链表的复制、排序链表、合并K个升序链表、LRU缓存

随机链表的复制 为了在 O(n) 时间复杂度内解决这个问题&#xff0c;并且使用 O(1) 的额外空间&#xff0c;可以利用以下技巧&#xff1a; 将新节点插入到原节点后面&#xff1a;我们可以将复制节点插入到原节点后面。例如&#xff0c;如果链表是 A -> B -> C&#xff0c…...

静态页面在安卓端可以正常显示,但是在ios打开这个页面就需要刷新才能显示全图片

这个问题可能有几个原因导致,我来分析一下并给出解决方案: 首要问题是懒加载实现方式的兼容性问题。当前的懒加载实现可能在 iOS 上不够稳定。建议修改图片懒加载的实现方式: // 使用 Intersection Observer API 实现懒加载 function initLazyLoading() {const imageObserver…...

四元数如何用于 3D 旋转(代替欧拉角和旋转矩阵)【ESP32指向鼠标】

四元数如何用于 3D 旋转&#xff08;代替欧拉角和旋转矩阵&#xff09; 在三维空间中&#xff0c;物体的旋转可以用 欧拉角、旋转矩阵 或 四元数 来表示。 四元数相比于欧拉角和旋转矩阵有 计算更高效、避免万向锁、存储占用少 等优点&#xff0c;因此广泛用于 游戏开发、机器…...

JavaScript 内置对象-日期对象

在JavaScript中&#xff0c;处理日期和时间是一个常见的需求。无论是显示当前时间、计算两个日期之间的差异&#xff0c;还是格式化日期字符串&#xff0c;Date 对象都能提供强大的支持。本文将详细介绍 Date 对象的使用方法&#xff0c;包括创建日期实例、获取和设置日期值、以…...

本地大模型编程实战(19)RAG(Retrieval Augmented Generation,检索增强生成)(3)

文章目录 准备创建矢量数据库对象创建 LangGraph 链将检索步骤转化为工具定义节点构建图 见证效果qwen2.5llama3.1MFDoom/deepseek-r1-tool-calling:7b 总结代码参考 上一篇文章我们演练了一个 用 langgraph 实现的 RAG(Retrieval Augmented Generation,检索增强生成) 系统。本…...

DeepSeek与ChatGPT:AI语言模型的全面对决

DeepSeek与ChatGPT&#xff1a;AI语言模型的全面对决 引言&#xff1a;AI 语言模型的时代浪潮一、认识 DeepSeek 与 ChatGPT&#xff08;一&#xff09;DeepSeek&#xff1a;国产新星的崛起&#xff08;二&#xff09;ChatGPT&#xff1a;AI 界的开拓者 二、DeepSeek 与 ChatGP…...

2024年年终总结

2024年终于过去了&#xff0c;这绝对是我人生中最惨痛的一年&#xff01;被小人欺骗、被庸人耽误、被自己蠢到&#xff01;不由的让我想起了22年那次算命&#xff0c;算命先生说我十年低谷期&#xff0c;如果从15年进创业公司开始&#xff0c;24年是最后一年&#xff0c;果然应…...

利用 Valgrind 检测 C++ 内存泄露

Valgrind 是一款运行在 Linux 系统上的编程工具集&#xff0c;主要用于调试和分析程序的性能、内存使用等问题。其中最常用的工具是 Memcheck&#xff0c;它可以帮助检测 C 和 C 程序中的内存管理错误&#xff0c;如内存泄漏、使用未初始化的内存、越界访问等。 安装 这里我以…...

Python中的HTTP客户端库:httpx与request | python小知识

Python中的HTTP客户端库&#xff1a;httpx与request | python小知识 在Python中&#xff0c;发送HTTP请求和处理响应是网络编程的基础。requests和httpx是两个常用的HTTP库&#xff0c;它们都提供了简洁易用的API来发送HTTP请求。然而&#xff0c;httpx作为新一代的HTTP客户端…...

【Python】Python入门基础——环境搭建

学习Python&#xff0c;首先需要搭建一个本地开发环境&#xff0c;或是使用线上开发环境&#xff08;各类练习网站&#xff09;&#xff0c;这里主要记录本地开发环境的配置。 目录&#xff1a; 一、下载和安装python解释器 官网下载地址&#xff1a;Download Python | Pytho…...

2025 pwn_A_childs_dream

文章目录 fc/sfc mesen下载和使用推荐 fc/sfc https://www.mesen.ca/docs/ mesen2安装&#xff0c;vscode安装zg 任天堂yyds w d 左右移动 u结束游戏 i崩溃或者卡死了 L暂停 D658地方有个flag 发现DEEE会使用他。且只有这个地方&#xff0c;maybe会输出flag&#xff0c;应…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...