【C++】从基类到派生类:关于C++继承的务实梳理
浮世尘弦个人主页⭐个人专栏《C语言》、《数据结构与算法》、《C》非淡薄无以明志非宁静无以致远前言在 C 中继承是面向对象编程的重要机制之一。它允许我们基于已有的类定义新的类从而复用现有代码并在原有基础上扩展新的属性和行为。被继承的类通常称为基类或父类继承得到的新类称为派生类或子类。不过继承并不只是“把成员拿过来”这么简单。继承方式会直接影响父类成员在子类中的访问权限对象的构造和析构也有固定顺序。理解这些规则是正确使用 C 继承的基础。接下来让我们详细学习一下继承的相关内容吧目录一继承的介绍1引入继承2继承的概念二基类访问方式变化1访问变化表2stack继承底层容器三基类和派生类之间的转换四子类的默认成员函数1默认成员函数2实现一个不能被继承的类五继承中地友元和静态成员1继承与友元函数2继承与静态成员六多继承及菱形继承问题1继承模式2虚继承3多继承中的指针偏移问题七继承和组合一继承的介绍1引入继承首先我们看一下下面这个案例定义这两个类时这两个类中有许多重复的部分成员和函数。这一部分的存在时冗余的。有什么方法可以帮助我们去除这些重复的部分就像之前的函数模版类模版一样C提出了他的方法继承测试设计一个Person类把重复的部分都放进去左边的Person为父类右边的student和Teacher为子类子类直接继承父类的成员属性和函数不用在单独进行实现。去除了重复的部分。接下来重新来详细认识一下继承2继承的概念继承是面向对象编程OOP的三大核心特征之一封装继承多态。允许我们在一个已有类父类/基类上增加成员函数和成员变量再创建一个新类子类/派生类。定义格式二基类访问方式变化1访问变化表父类成员访问权限public继承protected继承private继承父类的public成员子类的public成员子类的protected成员子类的private成员父类的protected成员子类的protected成员子类的protected成员子 类的private成员父类的private成员❌ 在子类中不可见❌ 在子类中不可见❌ 在子类中不可见1,基类private成员在派⽣类中无论以什么⽅式继承都是不可见的。这⾥的不可见是指基类的私有成员无论在子类中还是在子类外都是无法进行访问使用但其还是被继承到了派⽣类对象中。即子类中还是有这一个成员的。2子类中继承到的成员变量的访问方式取决于Min(父类的成员访问限定方式子类继承方式public protected private3使用关键字class时默认的继承方式是private使用关键字struct时默认的继承方式是public可以进行省略但是建议写出来。4实际使用时使用最多的时public继承protected和private继承使用到的很少。因此实际重要的只有public继承的2个。补充父类和子类中的成员函数函数名相同调用子类同名函数时父类同名函数就会隐藏。一般调用的是子类B中的fun如果想要在子类对象中调用父类对象当中的同名函数需要用对象.父类名::函数名()这种形式。代码演示:class Person { protected: string name 小明; string ID 2331323; }; class Student : public Person { public: void Print() { cout name endl; //这里需要指定一下才能访问父类同名成员 cout Person::name endl; //直接进行访问 cout ID endl; } protected: string name 超正经小李; }; int main() { Student st1; st1.Print(); return 0; }运行结果最好还是不要在子类起同名的函数。2stack继承底层容器之前直接在stackT,container在container处直接调用容器这种方式称为组合。现在可以使用继承的方式使用容器适配器stack演示一下//之前使用stack底层的模板的方式叫做组合 // 这种叫继承 //#define CON deque //#define CON list #define CON vector namespace att { //stack继承底层容器 templateclass T class stack : public CONT //stack继承底层容器 { public: void push(const T x) { CONT::push_back(x);//需要指定容器实例化 } //不指定实例化就找不到实例化的push_back(); void pop() { CONT::pop_back(); } const T top() { return CONT::back(); } bool empty() { return CONT::empty(); } }; } int main() { att::stackint st; st.push(3); st.push(6); st.push(9); st.push(2); while (!st.empty()) { cout st.top() ; st.pop(); } return 0; }运行结果三基类和派生类之间的转换public继承出来的子类对象可以赋值给父类的对象/指针/引用。这种方式称为切片。反过来父类对象不能赋值给子类原因子类对象中可能存在父类对象中不存在的成员而父类的成员子类则是全部继承所以可以赋值。代码演示四子类的默认成员函数1默认成员函数1子类的构造函数必须调用父类的构造函数。如果父类没有默认构造函数则需要在子类构造函数中的初始化列表中显式调用。构造时先调用父类构造再调用子类。2子类的拷贝构造函数必须调用父类的拷贝构造完成初始化。3子类的赋值重载operator必须调用父类的operator完成父类的复制。由于重名的隐藏问题需要指定作用域。4子类的析构函数会在清理完自己独有的成员后自动调用父类析构函数清理共有的父类中的成员。析构时先调用子类析构再调用父类析构。5子类的析构和父类的析构构成隐藏关系。代码演示父类class Person { public: Person(const string _name 雀巢)//默认构造 :name(_name) { cout Person(const string _name) endl; } //拷贝构造 Person(const Person p1) :name(p1.name) { cout Person(const Person p1) endl; } Person operator(const Person p1) { cout Person operator(const Person p1) endl; if (this ! p1) { name p1.name; } return *this; } ~Person() { cout ~Person() endl; } protected: string name; };子类构造函数 public: //如果使用默认构造函数 //1内置类型不确定 //2自定义类型调用默认构造 //3继承父类的成员看成一个整体调用父类构造 //构造函数 Student(const string name, int _age, const string _address) :Person(name) //使用父类初始化 ,age(_age) ,address(_address) {}拷贝构造//拷贝构造函数--可以不写使用默认的就可以 //但是有资源的开辟深拷贝就要自己写 Student(const Student s1) :Person(s1) ,age(s1.age) ,address(s1.address) { //深拷贝 }赋值重载operator://赋值重载函数--可以不写使用默认的就可以 //但是有资源的开辟深拷贝就要自己写 Student operator(const Student s) { if (this ! s) { Person::operator(s); age s.age; address s.address; } return *this; }析构函数//Student()析构默认生成的就够用了只有有资源要释放才手动写 ~Student() { //子类析构和父类析构构成隐藏关系 //子类调用后会自动调用父类析构 }测试案例和结果结果分析打印的结果为什么调用了两次拷贝构造 四次析构明明只有三个对象原因出在父类上的赋值重载上2实现一个不能被继承的类1C98做法让父类构造函数私有由于子类的构造函数必须调用父类的构造函数父类构造 函数私有化以后子类看不见就不能进行调用无法实例化对象。2C11新增加一个关键字finalfinal放在父类名字的后面这样子类就不能进行继承。代码演示 //实现一个不能被继承的类 //1父类构造函数私有子类的构造函数必须调用父类的构造函数C98) //2C11,final关键字final修改父类派生类就不能继承了 class A //final //第二种方法 { public: void Func1() { cout adls endl; } private: //第一种方法 //A() //{} }; class B : public A { public: void Func2() { cout Func2 endl; } }; int main() { B b1; b1.Func1(); return 0; }运行结果A类中除了中文去掉任意一个注释B类都不能正常初始化 。补充一个有趣的现象按需实例化当主函数不进行实例化时如下图左边展开final依旧会报错但是右边使用第一种方法私有化却不会进行报错这就是一种按需实例化。原因解释不进行实例化 编译器就不会进行仔细地检查只会大致检查是否有明显地错误。五继承中地友元和静态成员1继承与友元函数友元关系不能进行继承基类友元不能访问子类私有和保护成员代码演示class D //需要提前进行声明否则不知道C中的D是什么 //继承与友元 //友元关系不能进行继承基类友元不能访问子类私有和保护成员 class C { public: friend void play(const C s1, const D s2); protected: string name; }; class D : public C { public: //friend void play(const C s1, const D s2); protected: int age; }; void play(const C s1, const D s2) { cout s1.name endl; cout s2.age endl; } int main() { return 0; }这里友元函数play中的age会报错解决方法让子类当中也包括上友元函数2继承与静态成员父类定义了 staic静态成员整个继承体系中只有这一个静态成员 。无论继承出多少子类都只有这一个staic成员实例化。代码演示//静态成员与继承 class Person { public: string name; static int age; }; int Person::age 18; class Student : public Person { protected: string ID; }; int main() { Person p1; Student s2; //name是不同的父子类各用一份 cout p1.name endl; cout s2.name endl; //age是同一个父子类共用一个 cout p1.age endl; cout s2.age endl; //public前提下可以直接指定类域进行访问 cout Person::age endl; cout Student::age endl; Person::age; cout p1.age endl; cout s2.age endl; return 0; }运行展示六多继承及菱形继承问题1继承模式单继承一个子类只有一个直接父类这样的继承关系称之为单继承。多继承一个子类有两个或两个以上的直接父类这种继承关系称为多继承。先继承的父类在前面后继承的父类在后面。菱形继承菱形继承是多继承的一种特殊情况。菱形继承有数据冗余和二义性的问题。实践一般不使用菱形继承。结构如下所示解释Student和Person继承了PersonAssistant有继承了Student和Tercher那么从Person中继承到Assistant中的成员就有了两份造成了数据冗余和二义性。2虚继承虚继承是为了解决菱形继承专门设计的一种机制。在继承前面加上virtual代码演示//菱形继承与虚函数 class Person { public: Person(const string name) :_name(name) {} string _name; }; class Student : virtual public Person { public: Student(const string name,int num) :Person(name) ,_num(num) {} private: int _num;//学号 }; class Teacher : virtual public Person { public: Teacher(const string name,const string Id) :Person(name) ,ID(Id) {} private: string ID;//工号 }; class Assistant :public Student, public Teacher { public: //对象的构造顺序永远是虚基类Person - 非虚基类(Student和Tercher) - 自身成员变量 Assistant(int num,const string id,const string name) :Person(name)//编译器只认这里的name ,Student(name,num)//虚继承后这里的name会被编译器忽略 ,Teacher(name,id)//同样被忽略 {} private: int age; };运行测试总结与补充1只要直接的父类是同一个就加上virtual2,假设有五层继承A-B-C-D-E如果全是普通继承E的初始化列表中只能有D的初始化和自己成员的初始化。如果发生了虚继承需要初始化虚基类B上层直接父类自身成员。虚基类并不是一种特殊的类而是在虚继承关系中被virtual关键字修饰的那个共享基类。E中的初始化列表中会包含B,C,D以及自身成员的初始化。3多继承中的指针偏移问题结果C分析直接父类在子类中的初始化的顺序和继承的顺序相同。先继承Base1再继承Base2因此Base1在前Base2在后。七继承和组合1public继承是⼀种is-a的关系。每个子类对象都是⼀个父类对象。2组合是⼀种has-a的关系。假设B组合了A每个B对象中都有⼀个A对象。继承允许你根据基类的实现来定义派生类的实现。这种通过生成派⽣类的复用通常被称为白箱复用。继承⼀定程度破坏了基类的封装基类的改变对派生类有很大的影响。派生类和基类间的依 赖关系很强耦合度高。白箱在继承方式中基类的内部细节对派生类可见。对象组合实现新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用被称为黑箱复用。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系耦合度低。优先使用对象组合有助于保持每个类被封装。黑箱对象的内部细节是不可见的。总结1黑箱测试相对简单不用了解底层实现从功能的角度测试白箱测试相对较难要了解底层实现从代码运行逻辑测试。2优先使用组合而不是继承。实际尽量多去用组合组合的耦合度低代码维护性好。不过也不太 那么绝对类之间的关系就适合继承那就⽤继承另外要实现多态也必须要继承。类之间的关系既适合用继承也适合组合就用组合。结尾本篇文章中所涉及到的代码gitee链接继承1 菱形继承与虚继承以上就是本文的全部内容了如果对你有所帮助的话请大佬不妨给博主来个“一键三连”这对我是大大的支持也能让博主产出更优质的内容。往期回顾把值写进类型C 模板进阶的非类型参数、特化与分离编译总结本文系统讲解C继承机制涵盖继承概念、访问控制变化、基类与派生类转换、默认成员函数处理、友元与静态成员特性重点剖析多继承及菱形继承问题并引入虚继承解决方案。强调继承与组合的对比。⚡把上面的内容吃透就休息一下吧⚡

相关新闻

短视频配音工具哪个好用

短视频配音工具哪个好用

说明本文基于公开产品体验与多方使用反馈整理,不含任何商业合作,仅作为选型参考。文中产品均按公开信息描述,具体功能以各平台官方页面为准。结论先看短视频配音工具没有哪个绝对最好,选型的核心就是场景匹配。已经在用剪映做视频…

2026/10/12 3:52:20 阅读更多 →
微信自动回复突然全停了?五个检查点从外到内

微信自动回复突然全停了?五个检查点从外到内

早上打开后台的那一刻就知道不对劲:一整夜的咨询,一条自动回复都没有,买家的消息静静躺在列表里。自动回复「全停」和「偶发漏答」是两种问题——偶发漏答多半是词表的事,全停基本是链路断了。排查顺序很重要:从外到内…

2026/10/12 3:52:20 阅读更多 →
Python aifactory-alpha 包详解与实战案例

Python aifactory-alpha 包详解与实战案例

1. 引言aifactory-alpha 是一个面向 Python 开发者的 AI 工具包,旨在简化人工智能相关功能的集成与调用。它封装了常见的 AI 能力,如文本生成、图像处理、模型调用等,帮助开发者以更少的代码完成更多任务。本文将从功能、安装、语法、参数、实…

2026/10/12 3:51:19 阅读更多 →

最新新闻

Tortoise-ORM 与 Sanic 集成实战:register_tortoise 生命周期管理全解析

Tortoise-ORM 与 Sanic 集成实战:register_tortoise 生命周期管理全解析

数据库后端 【免费下载链接】tortoise-orm Familiar asyncio ORM for python, built with relations in mind 项目地址: https://gitcode.com/gh_mirrors/to/tortoise-orm 点击查看 免费下载 本文以 Tortoise-ORM 仓库中 Sanic 集成示例 为主线,系统讲解…

2026/10/12 6:02:32 阅读更多 →
从ABP到Clean DDD:中后台系统架构迁移实践与反思

从ABP到Clean DDD:中后台系统架构迁移实践与反思

做中后台和SaaS类系统的团队,大概率都绕不开 ABP 这个名字。它把模块化、仓储模式、工作单元、动态 API、审计日志、多租户这些东西一次性打包成开箱即用的起点,用 .NET 技术栈做内部系统,几乎第一天就能跑起来。我所在的团队也是这样起步的&…

2026/10/12 6:02:32 阅读更多 →
STM32 | CLion + ST-Link下载调试完整流程

STM32 | CLion + ST-Link下载调试完整流程

一、下载程序 先创建一个文件夹: 命名:stlink.cfg 写入以下代码: # choose st-link/j-link/dap-link etc. #adapter driver cmsis-dap #transport select swdsource [find interface/stlink.cfg]transport select hla_swdsource [find target/stm32f4x.…

2026/10/12 6:02:32 阅读更多 →
Python代码打包成exe文件详解

Python代码打包成exe文件详解

一、pyhon代码打包成exe文件1-1:安装打包工具在PyCharm底部的 终端(Terminal) 里输入:pip install pyinstaller1-2:输入打包命令在同一个终端里输入(直接复制):pyinstaller --onefile --noconsole --hidden…

2026/10/12 6:02:32 阅读更多 →
知识工作插件化:从信息捕获到配置同步的效率体系

知识工作插件化:从信息捕获到配置同步的效率体系

平时做知识工作,最耗时间的往往不是思考本身,而是信息的搬运。你从网页摘一段话,粘贴进笔记里,格式全乱;你复制了一段关键论述,过了几天想找来源,翻遍聊天记录和文档都找不到;你给十…

2026/10/12 6:02:32 阅读更多 →
DJL与Spring集成:Java后端部署深度学习模型的实践指南

DJL与Spring集成:Java后端部署深度学习模型的实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 6:01:32 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →