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

【C++】继承

🌹 作者: 云小逸
🤟 个人主页: 云小逸的主页
🤟 motto: 要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在!学会自己和解,与过去和解,努力爱自己。希望春天来之前,我们一起面朝大海,春暖花开!

🥇 专栏:

  • 动态规划
  • C 语言
  • C++
  • Java 语言
  • Linux 编程
  • 算法
  • 待续…

文章目录

    • @[toc]
    • 📚 前言
    • 一、继承基础:从代码复用说起
      • 1. 继承的本质与作用
      • 2. 继承定义格式与访问控制
    • 二、继承中的关键机制与细节
      • 1. 对象赋值与切片操作
      • 2. 作用域与成员隐藏
      • 3. 友元与静态成员
    • 三、子类默认成员函数的行为
      • 1. 构造与析构顺序
      • 2. 拷贝构造与赋值重载
    • 四、多继承与钻石继承问题
      • 1. 多继承的复杂性
      • 2. 虚继承解决钻石继承
    • 五、继承 vs 组合:如何选择?
    • 📣 结语

📚 前言

在面向对象编程中,继承是实现代码复用的核心机制之一。它允许我们在已有类(基类)的基础上创建新类(派生类),通过扩展或修改基类的特性,高效地构建层次化的类结构。本文将深入解析C++中继承的核心概念、使用场景及常见问题,帮助开发者理解如何合理运用继承提升代码质量。

一、继承基础:从代码复用说起

1. 继承的本质与作用

  • 核心价值:通过提取公共属性和方法到基类,派生类只需关注差异化逻辑,避免重复编码。
    举例:设计学生(Student)和教师(Teacher)类时,可将姓名、电话等通用信息提取到Person基类,派生类仅需添加学号(_stuID)或工号(_wordID)等专属成员。
class Person {
public:string _name;string _tel;void PrintInfo() { /* 打印通用信息 */ }
};class Student : public Person { // 公有继承基类
public:string _stuID; // 学生专属学号
};
  • 层次化设计:体现“由简单到复杂”的认知过程,如PersonStudentGraduateStudent的递进关系。

2. 继承定义格式与访问控制

  • 语法结构

    class 派生类 : 继承方式 基类 { /* 派生类成员 */ };
    

    其中继承方式包括public(公有)、protected(保护)、private(私有),默认值为privateclass关键字)或publicstruct关键字)。

  • 访问限定符影响

    • public继承:基类的public/protected成员在派生类中保持原有访问级别,private成员不可见(但仍被继承,只是无法访问)。
    • protected/private继承:基类的public成员在派生类中变为protectedprivate,具体规则可简化为“取继承方式与成员访问级别的最小值”(public > protected > private)。

二、继承中的关键机制与细节

1. 对象赋值与切片操作

  • 子类→父类赋值:合法,称为“切片”,派生类对象中基类部分会被复制到基类对象/指针/引用中。
    Student s;
    Person p = s; // 切片操作,仅复制s中的Person部分
    
  • 父类→子类赋值:非法,无法将基类对象直接赋值给派生类对象(需强制类型转换,但可能导致越界)。

2. 作用域与成员隐藏

  • 同名成员隐藏:若派生类与基类存在同名成员(变量或函数),派生类成员会覆盖基类成员(称为“隐藏”或“重定义”)。
    解决:通过基类::成员名显式访问基类成员。
    class Person { public: int _num; };
    class Student : public Person { public: int _num; }; // 隐藏基类的_numStudent s;
    s._num = 10; // 访问派生类的_num
    s.Person::_num = 20; // 显式访问基类的_num
    

3. 友元与静态成员

  • 友元不继承:基类的友元无法访问派生类的protected/private成员,反之亦然。
  • 静态成员共享:基类的静态成员在整个继承体系中只有一份实例,派生类可直接访问。
    class Person { public: static int count; };
    int Person::count = 0;
    class Student : public Person {};
    cout << Student::count; // 输出0,与基类共享静态成员
    

三、子类默认成员函数的行为

1. 构造与析构顺序

  • 构造顺序:先调用基类构造函数(初始化基类成员),再调用派生类构造函数(初始化派生类成员)。
    注意:若基类无默认构造函数,派生类需在初始化列表显式调用基类构造函数。

    class Person { public: Person(string name) : _name(name) {} };
    class Student : public Person {
    public:Student(string name, int stuID) : Person(name), _stuID(stuID) {} // 显式调用基类构造
    };
    
  • 析构顺序:与构造顺序相反,先派生类析构,再基类析构(自动调用,无需显式声明)。

2. 拷贝构造与赋值重载

  • 派生类的拷贝构造函数需显式调用基类拷贝构造函数,赋值运算符同理。
    Student::Student(const Student& s) : Person(s), _stuID(s._stuID) {} // 拷贝构造
    Student& Student::operator=(const Student& s) {Person::operator=(s); // 先赋值基类部分_stuID = s._stuID;return *this;
    }
    

四、多继承与钻石继承问题

1. 多继承的复杂性

  • 定义:一个派生类继承多个基类(如class Assistant : public Student, public Teacher)。
  • 问题
    • 二义性:当多个基类拥有同名成员时,派生类访问该成员会产生歧义,需用作用域符明确(如a.Student::_name)。
    • 数据冗余:钻石继承(菱形继承)中,顶层基类成员会在派生类中重复存储,浪费空间。

2. 虚继承解决钻石继承

  • 核心思想:通过virtual关键字让中间基类(如StudentTeacher)共享顶层基类(Person)的同一份实例,避免数据冗余。
    class Student : virtual public Person {}; // 虚继承
    class Teacher : virtual public Person {}; // 虚继承
    
  • 代价:虚继承会引入额外的指针(虚基类表指针),增加访问开销,但换取空间优化。

五、继承 vs 组合:如何选择?

特性继承(is-a)组合(has-a)
关系本质派生类是基类的一种(如“学生是人”)类包含另一个类的对象(如“汽车有轮胎”)
封装性基类细节对子类可见(白箱复用),耦合度高仅通过接口交互(黑箱复用),耦合度低
复用方式继承基类实现,依赖基类变化组合对象功能,依赖接口稳定性
推荐场景严格类型层级(如“动物→猫→波斯猫”)功能组合(如“日志类+网络类组合成通信模块”)

最佳实践:优先使用组合,除非明确满足“is-a”关系(如子类必须继承基类行为)。组合更灵活,便于维护,而继承常用于多态实现(后续多态专题会深入探讨)。

📣 结语

继承是C++面向对象编程的重要工具,合理使用能显著提升代码复用性,但也需警惕其带来的复杂性(如钻石继承、高耦合)。理解访问控制、成员函数行为及与组合的差异,是掌握继承的关键。记住:能用组合解决的问题,优先不使用继承,保持代码的简洁与可维护性。

如果你觉得本文对你有帮助,点赞、收藏、关注就是对我最大的支持!我们下期C++多态专题再见~

  • 技术之路没有捷径,唯有脚踏实地。
  • 每一次代码的重构,都是对过去自己的超越。
  • 保持好奇,持续学习,让技术成为改变世界的力量。
http://www.xdnf.cn/news/112393.html

相关文章:

  • 数据结构与算法实战:从理论到落地的深度探索
  • 原生微信小程序,canvas生成凭证,保存到手机
  • Java的进阶学习
  • 鲲鹏麒麟搭建Docker仓库
  • 海量聊天消息处理:ShardingJDBC分库分表、ClickHouse冷热数据分离、ES复合查询方案、Flink实时计算与SpringCloud集成
  • C++ RPC以及cmake
  • VBA技术资料MF300:利用Mid进行文本查找
  • 专家系统的一般结构解析——基于《人工智能原理与方法》的深度拓展
  • JBoltAI 赋能金融文档:基于 RAG 的基金招募说明书视觉增强方案
  • 分布式微服务架构,数据库连接池设计策略
  • 【框架学习】Spring AI-功能学习与实战(一)
  • node.js 实战——(Http 知识点学习)
  • 使用PyTorch如何配置一个简单的GTP
  • Framework.jar里的类无法通过Class.forName反射某个类的问题排查
  • FPGA上实现YOLOv5的一般过程
  • 机器学习特征工程中的数值分箱技术:原理、方法与实例解析
  • 看一看 中间件Middleware
  • mapbox高阶,高程影像、行政区边界阴影效果实现
  • 开源项目实战学习之YOLO11:ultralytics-cfg-datasets-lvis.yaml文件(五)
  • 长城杯铁人三项初赛-REVERSE复现
  • Linux常见指令介绍下(入门级)
  • 手搓雷达图(MATLAB)
  • Java24新增特性
  • C语言数据结构之顺序表
  • 从代码学习深度学习 - 图像增广 PyTorch 版
  • 解决VSCode每次SSH连接服务器时,都需要下载vscode-server
  • Rust 2025:内存安全革命与异步编程新纪元
  • 大模型技术全景解析:从基础架构到Prompt工程
  • 无感字符编码原址转换术——系统内存(Mermaid文本图表版/DeepSeek)
  • 7.9 Python+Click实战:5步打造高效的GitHub监控CLI工具