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

C++中的继承:从基础到复杂

目录

前言

1. 继承的基本概念

2. 继承方式与访问控制

3. 派生类与基类的对象转换

4. 继承中的作用域

5. 派生类的默认成员函数

6. 继承中的特殊关系

6.1 继承与友元

6.2 继承与静态成员

7. 复杂的菱形继承问题

8. 继承与组合的选择

9. 常见面试题

总结


前言

继承是面向对象编程中最重要的概念之一,它允许我们创建新的类(派生类)基于已有类(基类)的特性进行扩展。在C++中,继承机制提供了代码复用的强大手段,同时也带来了许多需要注意的细节和复杂性。本文将全面介绍C++中的继承机制,从基本概念到高级应用,帮助读者深入理解这一重要主题。


1. 继承的基本概念

继承允许派生类复用基类的成员(包括成员变量和成员函数),同时可以添加新的特性或修改现有行为。这种机制体现了面向对象编程中"由简单到复杂"的认知过程。
 

class Person {
public:void Print() {cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "peter";int _age = 18;
};class Student : public Person {
protected:int _stuid; // 学号
};


 

在这个例子中,`Student`类通过公有继承获得了`Person`类的所有成员,同时添加了自己的特有成员`_stuid`。

2. 继承方式与访问控制

C++提供了三种继承方式:public、protected和private。不同的继承方式会影响基类成员在派生类中的访问权限。

基类成员/继承方式public继承protected继承private继承
public成员publicprotectedprivate
protected成员protectedprotectedprivate
private成员不可见不可见不可见

重要规则:
1. 基类的private成员在派生类中不可见
2. 成员在派生类中的访问权限 = min(成员在基类的访问权限, 继承方式)
3. class默认private继承,struct默认public继承(但建议显式指定)

3. 派生类与基类的对象转换

派生类对象可以赋值给基类对象/指针/引用,这种现象称为"切片"或"切割"。


 

Student sobj;
Person pobj = sobj;  // 切片
Person* pp = &sobj;  // 指针
Person& rp = sobj;   // 引用


 

但反过来不行,除非使用强制类型转换(需谨慎使用)。

4. 继承中的作用域

基类和派生类有独立的作用域。当派生类与基类有同名成员时,派生类成员会隐藏基类成员(称为"隐藏"或"重定义")。
 

class Person {
protected:int _num = 111; // 身份证号
};class Student : public Person {
public:void Print() {cout << "身份证号:" << Person::_num << endl; // 显式访问cout << "学号:" << _num << endl;}
protected:int _num = 999; // 学号
};


 

5. 派生类的默认成员函数

派生类的6个默认成员函数有其特殊性:

1. 构造函数必须调用基类构造函数初始化基类部分
2. 拷贝构造必须调用基类拷贝构造
3. operator=必须调用基类operator=
4. 析构函数会自动调用基类析构函数(先派生后基类)
5. 对象初始化顺序:先基类构造,再派生类构造
6. 对象析构顺序:先派生类析构,再基类析构

6. 继承中的特殊关系

6.1 继承与友元

友元关系不能继承,基类的友元不能访问派生类的私有和保护成员。

6.2 继承与静态成员

基类定义的静态成员在整个继承体系中只有一个实例,无论派生出多少子类。
 

class Person {
public:static int _count; // 统计人数
};
int Person::_count = 0;class Student : public Person { /*...*/ };
class Graduate : public Student { /*...*/ };// 所有类共享同一个_count


 

7. 复杂的菱形继承问题

菱形继承是多继承的一种特殊情况,会导致数据冗余和二义性问题。


class Person { /*...*/ };
class Student : public Person { /*...*/ };
class Teacher : public Person { /*...*/ };
class Assistant : public Student, public Teacher { /*...*/ };

 

解决方案是使用**虚拟继承**:


 

class Student : virtual public Person { /*...*/ };
class Teacher : virtual public Person { /*...*/ };
class Assistant : public Student, public Teacher { /*...*/ };


 

虚拟继承通过虚基表指针和虚基表解决数据冗余和二义性问题。

8. 继承与组合的选择

- 继承表示"is-a"关系(如BMW是一种Car)
- 组合表示"has-a"关系(如Car有Tire)

设计原则:
1. 优先使用对象组合而非类继承
2. 继承会破坏封装,增加耦合度
3. 组合保持类封装,耦合度低
4. 需要多态时必须使用继承

 

// 继承示例
class BMW : public Car { /*...*/ };// 组合示例
class Car {
protected:Tire _t; // 轮胎
};


 

9. 常见面试题

1. 什么是菱形继承?它的问题是什么?
2. 虚拟继承如何解决数据冗余和二义性?
3. 继承和组合的区别?何时使用它们?


总结

C++的继承机制强大但复杂,特别是多继承和菱形继承。理解继承的各种细节对于编写健壮、可维护的面向对象代码至关重要。在实际开发中,应当谨慎使用多继承,优先考虑组合而非继承,只有在确实需要表达"is-a"关系或实现多态时才使用继承。

通过本文的学习,希望读者能够掌握C++继承的核心概念,理解其底层原理,并能够在实际项目中做出恰当的设计选择。

http://www.lryc.cn/news/614687.html

相关文章:

  • 飞算JavaAI深度解析:专为Java生态而生的智能引擎
  • 安全引导功能及ATF的启动过程(四)
  • 巧妙实现Ethercat转Profinet协议网关匹配光伏电站
  • 「ECG信号处理——(22)Pan-Tompkins Findpeak 阈值检测 差分阈值算法——三种R波检测算法对比分析」2025年8月8日
  • C语言编译流程讲解
  • 【Open3D】基础操作之三维数据结构的高效组织和管理
  • 内网穿透原理与部署实战指南:从理论到企业级应用
  • 第七章:数据持久化 —— `chrome.storage` 的记忆魔法
  • 2025 蓝桥杯C/C++国B 部分题解
  • 设计一个 Java 本地缓存组件
  • java分布式定时任务
  • 秋招笔记-8.8
  • BGP协议笔记
  • 6_基于深度学习的火灾检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • 腾讯前端面试真题
  • 锯床自动长度检测与参数闭环补偿系统
  • 坚鹏:AI智能体辅导是知行学成为AI智能体创新应用引领者的保障
  • 计算机网络:到底什么是可变长子网掩码VLSM?
  • Linux初级阶段性练习
  • 移动端开发中类似腾讯Bugly的产品推荐与比较-5款APP异常最终产品推荐-卓伊凡|bigniu
  • A100用transformers推理gpt-oss
  • 第六章第四节 PWM驱动LED呼吸灯 PWM驱动舵机 PWM驱动直流电机
  • 校招秋招春招小米在线测评小米测评题库|测评解析和攻略|题库分享
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘huggingface_hub’问题
  • Numpy科学计算与数据分析:Numpy高效数据处理与优化
  • Mac 电脑放在环境变量中的通用脚本
  • 免费PDF批量加密工具
  • 从零掌握 Java AWT:原理、实战与性能优化
  • 【沉浸式解决问题】pycharm关闭科学模式
  • 杰理ac791 [Info]: [LL_S]Recv - LL_CHANNEL_MAP_REQ