c++ 继承 和 组合
目录
一. 继承
1.1 继承的概念
1.2 继承定义
1.3 继承类模板
1.4. 继承中的作用域
二. 派生类(子类)的默认成员函数
2.1 概念:
2.2 实现⼀个不能被继承的类
2.3 继承与友元
2.4继承与静态成员
三.多继承及其菱形继承问题
3.1继承方式:
3.2 虚继承
四.继承和组合
一. 继承
1.1 继承的概念
继承(inheritance)机制是⾯向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数) 和 属性(成员变量),这样产生新的类,称派生类
1.2 继承定义
下⾯我们看到Person是基类,也称作父类。man是派生类,也称作子类。(因为翻译的原因,所以既叫基类/派生类,也叫父类/子类)
继承的结构
对比 访问限定符 和 继承方式:
继承方式的作用:
基类 的private的任何成员 都无法被访问
继承方式为 public时,子类 可以使用 父类的protect 和 public 成员
继承方式为 protected时,子类 可以使用 父类的protect 和 public 成员
具体来说:
- protected: 只有在该类及其子类中可以访问,外部类不能访问。
- public: 可以在任何地方访问,不受限制。
(在实际运用中⼀般使用都是public继承)!!!
1.3 继承类模板
自定义的类 可以继承 库里面的类
比如 :
1.4. 继承中的作用域
1.4.1 隐藏规则:
1. 在继承体系中 基类 和 派生类 都有独立的作用域。
2. 派生类和基类中有同名成员,派生类成员将 屏蔽 基类对同名成员的 直接访问!,这种情况叫隐藏。(在派生类成员函数中,可以使用基类::基类成员显示访问)
3. 需要注意的是如果是成员函数的隐藏,只需要 函数名相同! 就构成隐藏。
4. 注意在实际中在继承体系里面最好不要定义同名的成员。
比如:自动调用b的fun,因为A类的自动隐藏了
这个也构成隐藏
二. 派生类(子类)的默认成员函数
2.1 概念:
1. 派生类的构造函数 必须调用基类的构造函数 初始化基类的那⼀部分成员。如果 基类没有默认的构造函数,则必须在 派生类构造函数 的初始化列表阶段显示调用。
2. 派生类的 拷贝构造函数 必须调用基类的拷贝构造 完成 基类的拷贝初始化。
3. 派生类的析构函数 会在被调用 完成后 自动调用 基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
4. 派生类 对象初始化先调用基类构造 再调 派生类构造。(先父后子 构造)
5. 派生类 对象析构 清理先调用派生类析构 再 调基类的析构 。(先子后父 析构)
6. 因为多态中⼀些场景析构函数需要构成重写,重写的条件之⼀是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destructor(),所以基类析构函数不加 virtual的情况下,派生类的 析构函数和基类的析构函数构成隐藏关系。
2.2 实现⼀个不能被继承的类
方法1:让基类的构造函数私有,派生类的构成必须调用 基类的构造函数,但是基类的构成函数私有化以后,派生类看不见就不能调用了,那么派生类就无法实例化出对象。
方法2:C++11新增了⼀个final关键字,final修改基类,派生类就不能继承了。
2.3 继承与友元
友元关系不能继承,也就是说基类的友元 不能访问 派生类的私有和保护成员
2.4继承与静态成员
基类定义了static静态成员,则整个继承体系里面只有⼀个这样的成员。无论派生出多少个派生类,都只有⼀个static成员实例。
三.多继承及其菱形继承问题
3.1继承方式:
3.1单继承:⼀个派生类只有⼀个直接基类时称这个继承关系为单继承:
3.2多继承:⼀个派生类有两个或 以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型是,先继承的基类在前面,后面继承的基类在后面,派生类成员在放到最后面:
3.3菱形继承:菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下面的对象成员模型构造,可以看出菱形继承有数据冗余和⼆义性的问题,在Assistant的对象中Person成员会有两份。支持多继承就⼀定会有菱形继承,像 Java就直接不支持多继承,规避掉了这里的问题,所以实践中我们也是不建议设计出菱形继承这样的模型的
3.2 虚继承
概念 : 虚继承(Virtual Inheritance)是面向对象编程中的一个概念,主要用于C++等编程语言中,解决多重继承时可能出现的“菱形继承”问题。
使用virtual
关键字,可以实现虚继承。
它可以 避免了重复的基类对象。这样做能够确保类的设计更加清晰,减少内存的使用和运行时的二义性问题。
四.继承和组合
4.1 public继承是⼀种 is-a的关系。也就是说每个派生类对象都是⼀个基类对象。
继承:
4.2 组合是⼀种 has-a的关系。假设B组合了A,每个B对象中都有⼀个A对象。
组合:
• 优先使用组合,而不是继承。实际尽量多去用组合,组合的耦合度低,代码维护性好。不过也不太那么绝对,如果 类之间的关系就适合继承(is-a)那就用继承,另外要实现多态,也必须要继承。
如果 类之间的关系 既适合用继承(is-a)也适合组合(has-a),就用组合