【C++】继承

🌹 作者: 云小逸

🤟 个人主页: 云小逸的主页

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

🥇 专栏:

### 文章目录

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

📚 前言

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

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

1. 继承的本质与作用

  • 核心价值 :通过提取公共属性和方法到基类,派生类只需关注差异化逻辑,避免重复编码。
    举例 :设计学生(Student)和教师(Teacher)类时,可将姓名、电话等通用信息提取到Person基类,派生类仅需添加学号(_stuID)或工号(_wordID)等专属成员。
cpp 复制代码
class Person {
public:
    string _name;
    string _tel;
    void PrintInfo() { /* 打印通用信息 */ }
};

class Student : public Person { // 公有继承基类
public:
    string _stuID; // 学生专属学号
};
  • 层次化设计 :体现"由简单到复杂"的认知过程,如PersonStudentGraduateStudent的递进关系。

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

  • 语法结构

    cpp 复制代码
    class 派生类 : 继承方式 基类 { /* 派生类成员 */ };

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

  • 访问限定符影响

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

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

1. 对象赋值与切片操作

  • 子类→父类赋值 :合法,称为"切片",派生类对象中基类部分会被复制到基类对象/指针/引用中。

    cpp 复制代码
    Student s;
    Person p = s; // 切片操作,仅复制s中的Person部分
  • 父类→子类赋值:非法,无法将基类对象直接赋值给派生类对象(需强制类型转换,但可能导致越界)。

2. 作用域与成员隐藏

  • 同名成员隐藏 :若派生类与基类存在同名成员(变量或函数),派生类成员会覆盖基类成员(称为"隐藏"或"重定义")。
    解决 :通过基类::成员名显式访问基类成员。

    cpp 复制代码
    class Person { public: int _num; };
    class Student : public Person { public: int _num; }; // 隐藏基类的_num
    
    Student s;
    s._num = 10; // 访问派生类的_num
    s.Person::_num = 20; // 显式访问基类的_num

3. 友元与静态成员

  • 友元不继承 :基类的友元无法访问派生类的protected/private成员,反之亦然。

  • 静态成员共享 :基类的静态成员在整个继承体系中只有一份实例,派生类可直接访问。

    cpp 复制代码
    class Person { public: static int count; };
    int Person::count = 0;
    class Student : public Person {};
    cout << Student::count; // 输出0,与基类共享静态成员

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

1. 构造与析构顺序

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

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

2. 拷贝构造与赋值重载

  • 派生类的拷贝构造函数需显式调用基类拷贝构造函数,赋值运算符同理。

    cpp 复制代码
    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)的同一份实例,避免数据冗余。

    cpp 复制代码
    class Student : virtual public Person {}; // 虚继承
    class Teacher : virtual public Person {}; // 虚继承
  • 代价:虚继承会引入额外的指针(虚基类表指针),增加访问开销,但换取空间优化。

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

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

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

📣 结语

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

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

  • 技术之路没有捷径,唯有脚踏实地。
  • 每一次代码的重构,都是对过去自己的超越。
  • 保持好奇,持续学习,让技术成为改变世界的力量。
相关推荐
菜鸟射手1 分钟前
QT creater和vs2017文件路径问题
linux·c++·windows·qt
wuqingshun3141598 分钟前
蓝桥杯17. 机器人塔
c++·算法·职场和发展·蓝桥杯·深度优先
是发财不是旺财9 分钟前
跟着deepseek学golang--认识golang
开发语言·后端·golang
simple_whu21 分钟前
解决编译pcl时报错‘chrono_literals‘: is not a member of ‘std‘
c++·windows·visual studio
Bruce_Liuxiaowei44 分钟前
基于Python+Flask的MCP SDK响应式文档展示系统设计与实现
开发语言·python·flask·mcp
chuxinweihui1 小时前
数据结构——栈与队列
c语言·开发语言·数据结构·学习·算法·链表
我不是程序猿儿1 小时前
[C#]反射的实战应用,实际数据模拟
开发语言·c#
wt_cs1 小时前
身份证实名认证接口数字时代的信任基石-node.js实名认证集成
开发语言·node.js·php
爱编程的鱼1 小时前
C# 结构(Struct)
开发语言·人工智能·算法·c#
只可远观2 小时前
Flutter Dart 循环语句 for while do..while break、continue
开发语言·javascript·ecmascript