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

C++:继承[下篇]

记录我自己零开始学习C++。

目录

一、继承与友元

二、继承与静态成员

三、复杂的菱形继承及菱形虚拟继承

3.1 单继承:一个子类只有一个直接父类时称这个继承关系为单继承。

3.2 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。

3.3.菱形继承:菱形继承是多继承的一种特殊情况。

3.4.解决方法

3.4.1访问成员变量有二义性时,可指定类域。

四、继承的总结和反思

一、继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员 。

二、继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。

class Person
{
public:Person() { ++_count; }
protected:string _name; // 姓名
public:static int _count; // 统计人的个数。
};int Person::_count = 0;class Student : public Person
{
protected:int _stuNum; // 学号
};int main()
{Person p;Student s;cout << &Person::_count << endl;cout << &Student::_count << endl;return 0;
}
在上述代码中,静态成员属于整个类(而非某个对象),派生类不会创建新的静态成员实例,而是与基类共享同一个静态成员。因此,Person::_count 和 Student::_count 本质上是同一个变量,输出的地址会完全相同。

三、复杂的菱形继承及菱形虚拟继承

3.1 单继承:一个子类只有一个直接父类时称这个继承关系为单继承。

3.2 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。

3.3.菱形继承:菱形继承是多继承的一种特殊情况。

菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。
class Person
{
public:string _name; // 姓名int _id;int _tel;string _adress;
};class Student : public Person
{
protected:int _num; //学号
};class Teacher : public Person
{
protected:int _id; // 职工编号
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};int main()
{//数据冗余和二义性Assistant a;//a._name = "小李";//a._name = "李老师";//1.指定类域a.Student::_name = "小李";a.Teacher::_name = "李老师";return 0;
}

由监视窗口得出数据的冗余:
在Assistant的对象中Person成员会有两份

3.4.解决方法
3.4.1访问成员变量有二义性时,可指定类域
int main()
{//数据冗余和二义性Assistant a;//a._name = "小李";//a._name = "李老师";//1.指定类域a.Student::_name = "小李";a.Teacher::_name = "李老师";return 0;
}

4.2 虚拟继承可以解决菱形继承的二义性和数据冗余的问题

虚拟继承:在继承会造成冗余的类的那里加上关键字 virtual

class Person
{
public:string _name; // 姓名int _id;int _tel;string _adress;
};class Student : virtual public Person
{
protected:int _num; //学号
};class Teacher : virtual public Person
{
protected:int _id; // 职工编号
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};int main()
{Assistant a;a.Student::_name = "小李";a._name = "小李";a._name = "李老师";return 0;
}

四、继承的总结和反思

1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱
形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设
计出菱形继承。否则在复杂度及性能上都有问题。
2. 多继承可以认为是C++的缺陷之一,很多后来的OO语言都没有多继承,如Java。
3. 继承和组合
(1)public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
(2)组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
(3)优先使用对象组合,而不是类继承 。
(4)继承:白箱复用,强耦合的 "is-a" 关系
继承的核心是 “基于基类定义派生类”,本质是一种 “is-a”(是一个)的关系(例如 “狗是一种动物”)。

白箱复用:派生类可以直接访问基类的内部细节(如 protected 成员),基类的实现对派生类是 “可见” 的。这种可视性虽然简化了派生类的实现(直接复用基类逻辑),但也破坏了基类的封装性—— 基类的私有实现本应被隐藏,却因继承被派生类间接依赖。
高耦合:派生类的实现严重依赖基类。如果基类的逻辑(如方法实现)发生变化,即使接口没变,派生类也可能出错;反之,修改派生类时也可能需要调整基类(尤其当基类设计不严谨时)。

(5) 组合:黑箱复用,低耦合的 "has-a" 关系
组合的核心是 “通过组装已有对象实现新功能”,本质是 “has-a”(有一个)的关系(例如 “汽车有一个发动机”)。

黑箱复用:被组合的对象通过 “接口” 与组合类交互,其内部实现对组合类完全隐藏(类似 “黑箱”)。组合类只关心被组合对象能做什么(接口),不关心怎么做(实现)。
低耦合:组合类与被组合对象之间仅通过接口依赖,彼此独立。即使被组合对象的内部实现大幅修改,只要接口不变,组合类就无需调整;反之,修改组合类也不会影响被组合对象。这种低耦合性极大提升了代码的可维护性和扩展性。

(6)优先组合,合理使用继承
优先用组合:大部分场景下,组合的低耦合、高封装特性更符合 “开闭原则”(对扩展开放,对修改关闭)。例如,实现 “学生有一个身份证” 时,用组合(学生类包含身份证对象)比让 “学生继承身份证” 更合理。
必要时用继承:当类之间确实是 “is-a” 关系(如 “正方形是一种图形”),或需要通过继承实现多态(基类引用指向派生类对象)时,继承是合适的。例如,定义 “Shape” 基类,让 “Circle”“Rectangle” 继承它,通过基类指针统一调用 “draw ()” 方法,实现多态行为。

http://www.dtcms.com/a/323437.html

相关文章:

  • Vue 使用element plus组件库提示doesn‘t work properly without JavaScript enabled
  • [ MySQL 数据库 ] 多表关联查询
  • STM32HAL库 -- 10.DMA外设实战(UART串口+DMA读取传感器数据)
  • Tangram官网教程
  • Qt Graphics View框架概述
  • 夺宝奇兵 古老之圈 送修改器(The Great Circle)免安装中文版
  • openvela之STM32开发板部署
  • 力扣(轮转数组)
  • 智慧水务漏检率↓75%:陌讯水下视觉监测方案实战解析
  • 北京天津唐山廊坊沧州打捞日记
  • Nvidia 开源 KO 驱动 开发入门
  • 车流高峰漏检率↓85%!陌讯时序建模方案在智慧交通的实时优化​
  • AtCoder Beginner Contest 418
  • LLVM编译器入门
  • 力扣面试150(51/100)
  • 【Python 工具人快餐 · 第 2 份】
  • 使用SPM进行核磁数据预处理
  • 【无标题】六边形结构在二维拓扑量子色动力学模型中确实具有独特优势,并构建完整的二维拓扑量子色动力学模型。
  • Redis三种特殊数据类型
  • 【深度学习2】logistic回归以及梯度下降
  • synchronized和RentrantLock用哪个?
  • Datawhale AI夏令营第三期,多模态RAG方向 Task2
  • 小白成长之路-Docker部署
  • 第二十八天(cookiesessiontokeny验证)
  • JVM性能调优的原则有哪些?
  • 深入理解C++构造函数与初始化列表
  • P1025 [NOIP 2001 提高组] 数的划分 题解
  • 【嵌入式DIY实例-Arduino篇】-水质检测系统
  • SQL面试题及详细答案150道(01-20) --- 基础概念与语法篇
  • python踩坑之识别错误...