news 2026/5/1 9:55:34

C++继承

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++继承

一.继承的概念

继承是一种可以让代码复用的机制,它在保持原有类结构的基础上进行拓展,增加方法和变量形成新的类,称为派生类。派生类继承的叫做基类。

继承定义格式

继承按照访问权限符分类
类成员/继承方法public继承protect继承private继承
基类public派生类的public成员派生类的protect成员派生类的private成员
基类protect派生类的protect成员派生类的protect成员派生类的private成员
基类private派生类不可见派生类不可见派生类不可见

这样做的目的是protect可以让子类进行访问的到同时保证类外不会访问到protect的成员,虽然基类private派生类不可见,但派生类仍然继承了基类的成员

  • 基类的private成员在派生类中无论以何种方式继承都是不可见的。这种不可见性意味着基类的私有成员虽然会被继承到派生类对象中,但在语法上禁止派生类对象(无论在类内部还是外部)访问这些成员。

  • 基类的private成员在派生类中不可访问。若希望基类成员不能被类外直接访问,但允许派生类访问,则应将其定义为protected。可见,protected访问限定符正是为继承场景而设计的。

  • 通过总结可以发现:基类的私有成员在派生类中始终不可见。对于基类的其他成员,在派生类中的访问权限等于成员在基类的访问限定符与继承方式中的较小者,遵循public > protected > private的优先级规则。

  • class关键字默认使用private继承方式,struct关键字默认使用public继承方式。但最佳实践是显式声明继承方式。

  • 实际开发中主要采用public继承,极少使用protected/private继承。因为这两种继承方式会导致继承的成员只能在派生类内部使用,不利于代码的扩展和维护。

二.基类与派生类的转化

  • public继承的派生类对象可以赋值给基类的指针/引用。把派生类的基类部分切片给基类指针/引用
  • 基类对象不能赋值给派生类对象
  • 基类的指针/引用可以强制转换类型赋值给派生类的指针/引用。但必须是基类指针指向派生类对象才是安全的。意思就是创建基类类型的指针指向派生类,而不是创建基类类型指针指向基类后又把这个指针转给派生类指针。

三.继承中的作用域

  • 继承体系里基类和派生类有独立的作用域
  • 派生类与基类有同名成员,派生类成员会隐藏基类的同名成员,叫做隐藏,对于隐藏的函数可以指定类域访问
  • 成员函数只要同名就会构成隐藏

四.派生类默认成员函数

  • 派生类构造函数必须调用基类的构造函数用来初始化基类的成员。若基类没有默认的构造函数,则派生类必须在初始化列表初始化。
  • 派生类的拷贝构造函数必须调用基类的拷贝构造函数完成基类的拷贝构造。
  • 派生类的operator=必须显式调用基类的operator=来完成基类部分的复制。需要注意的是,派生类的operator=会隐藏基类的operator=,因此在调用时需要指定基类作用域。

  • 派生类的析构函数执行完毕后会自动调用基类的析构函数来清理基类成员。这种机制确保了对象销毁时遵循先清理派生类成员、再清理基类成员的正确顺序。

  • 派生类初始化对象先调用基类构造在调用派生类构造

  • 析构先调用派生类析构再调用基类析构

  • 由于多态需要析构函数构成重写,导致析构函数处理成destructor导致父类子类会隐藏父类析构函数(在基类不加virtual下)

实现不可被继承类
  1. 可以将类名后加final这代表此类无法被继承
  2. 也可以将类的默认构造函数用private 让子类无法访问,就无法被继承了
继承和友元

父类的友元不会被子类继承

继承与静态成员

基类定义一个static成员则整个继承关系中只有这一个这样的成员

五.多继承

单继承:当一个派生类仅有一个直接基类时,这种继承关系称为单继承。

多继承:若一个派生类拥有两个或更多直接基类,则称为多继承。在多继承中,对象的内存布局遵循继承顺序:先继承的基类位于内存前部,后继承的基类依次排列,派生类成员则置于最后。

菱形继承:这是多继承中的特殊情形。从对象成员模型分析可见,菱形继承会导致数据冗余和二义性问题(如Assistant对象中包含两份Person成员)。由于多继承必然存在菱形继承问题,部分语言(如Java)选择直接禁用多继承来规避此问题。因此在实际开发中,应当避免设计菱形继承结构。

虚继承

因为多继承导致棱形继承二义性所以就有了虚继承。

虚继承是C++中解决多重继承带来的"菱形继承"问题的一种机制。它通过确保基类在继承体系中只被继承一次来避免数据冗余和歧义。

在多重继承中,当派生类通过不同路径继承同一个基类时,会产生"菱形继承"问题。例如:

class Base { public: int data; }; class Derived1 : public Base { // 继承Base }; class Derived2 : public Base { // 继承Base }; class Final : public Derived1, public Derived2 { // 通过Derived1和Derived2间接继承了两个Base };

这种情况下,Final类中将包含两个Base子对象,导致:

  1. 数据冗余 - 两份Base成员变量
  2. 访问歧义 - 无法直接访问Base成员,必须通过特定路径
虚继承的解决方案

使用virtual关键字声明继承关系:

class Derived1 : virtual public Base { // 虚继承Base }; class Derived2 : virtual public Base { // 虚继承Base }; class Final : public Derived1, public Derived2 { // 现在只包含一个Base子对象 };
实现原理
  1. 虚基类指针(vbase_ptr):编译器为每个虚继承的类添加一个指针,指向共享的基类子对象
  2. 虚基类表(vbtable):存储虚基类偏移量信息
  3. 共享实例:确保整个继承体系中只有一个基类实例

示例代码

#include <iostream> class Animal { public: Animal() { std::cout << "Animal constructor\n"; } void breathe() { std::cout << "Breathing...\n"; } }; class Mammal : virtual public Animal { public: Mammal() { std::cout << "Mammal constructor\n"; } }; class WingedAnimal : virtual public Animal { public: WingedAnimal() { std::cout << "WingedAnimal constructor\n"; } }; class Bat : public Mammal, public WingedAnimal { public: Bat() { std::cout << "Bat constructor\n"; } }; int main() { Bat bat; bat.breathe(); // 没有歧义,因为只有一个Animal实例 return 0; }

输出结果:

Animal constructor Mammal constructor WingedAnimal constructor Bat constructor Breathing...

六.继承和组合

继承与组合的区别
继承关系
  • public继承体现的是"is-a"关系,即每个派生类对象本质上都是一个基类对象。
  • 继承允许基于基类实现来定义派生类,这种复用方式称为白箱复用(white-box reuse)。"白箱"指基类的内部细节对派生类可见。
  • 继承会破坏基类封装性:基类的修改会显著影响派生类,两者之间存在强依赖关系,耦合度高。
组合关系
  • 组合体现的是"has-a"关系,例如类B组合类A时,每个B对象都包含一个A对象。
  • 组合是继承之外的另一种复用方式,通过组装对象实现更复杂功能,要求被组合对象有良好定义的接口。
  • 这种复用称为黑箱复用(black-box reuse),因为对象内部细节不可见,仅通过接口交互。
  • 组合类之间依赖关系弱,耦合度低,有助于保持类的封装性。
使用建议
  1. 优先使用组合:组合耦合度低,代码更易维护
  2. 继承适用场景
    • 当类之间确实是"is-a"关系时
    • 需要实现多态功能时
  3. 权衡选择:当关系既适合继承又适合组合时,优先选择组合方式
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 7:14:24

量化交易的思路

量化交易&#xff1a;用数据与模型重构投资逻辑在投资市场的演进中&#xff0c;从“凭经验选股”到“用数据决策”的转变&#xff0c;催生了量化交易这一核心范式。它以数学模型为骨架、以海量数据为血肉&#xff0c;将投资逻辑转化为可执行的代码&#xff0c;在波动的市场中寻…

作者头像 李华
网站建设 2026/5/1 8:36:07

目前最好的三折叠屏手机:三星Galaxy Z TriFold何以引领体验革命?

三折叠屏手机的出现&#xff0c;是否意味着移动设备的终极形态已近在眼前&#xff1f;当消费者不再满足于单一的折叠体验&#xff0c;对屏幕灵活性、性能与耐用性的要求愈发苛刻&#xff0c;一款真正的“最好”产品该具备怎样的特质&#xff1f;三星Galaxy Z TriFold的到来&…

作者头像 李华
网站建设 2026/4/29 17:49:20

AI大模型应用开发学习-22【20251213】

学习内容&#xff1a; &#x1f449;课程主题&#xff1a;《Pytorch与视觉检测》 ✅ PyTorch的核心概念 PyTorch的张量与自动求导机制PyTorch的动态图与静态图 ✅ PyTorch的分布式训练在多个GPU上进行训练使用PyTorch Lightning简化模型训练 ✅ 图像识别技术与缺陷检测传统图像…

作者头像 李华
网站建设 2026/4/22 2:11:10

C++初阶9:list使用攻略

目录 前言&#xff1a;为什么需要list 二、基础认知&#xff1a;list的底层与初始化 2.1什么是list 2.2头文件与命名空间 2.3初始化方式 三、迭代器误区 四、核心操作&#xff1a;增删查改 4.1元素添加&#xff1a;push_back/push_front/insert 4.2元素删除&#xff1…

作者头像 李华
网站建设 2026/5/1 0:54:09

重构智慧书-第14条:现实与风度

一、原文呈现现实与风度肚里有真货还不能说万事大吉:你还得留心与环境相配合。风度不佳会弄得百事尴尬&#xff0c;连正义与正理都会让它搅得变味。如果你风度可人&#xff0c;往往一美遮百丑:即使回绝人的“不”字&#xff0c;也让人感到堂而皇之&#xff0c;它能使真理甘甜&a…

作者头像 李华