一、继承与友元
友元关系不能够继承(就像父亲的朋友不一定是自己的朋友)
具体实现起来就是父类的友元可以访问父类的成员,但是不可以访问子类的成员
二、继承与静态成员
子类的静态成员变量本质上与父类的是同一份,存储在静态区中,所以可以继承
例如
class Person
{
public:static int _count = 0
}
可以直接继承到子类
三、单继承与多继承
3.1单继承是什么?
一个子类只有一个直接父类,如:
class Student : public Person
{}
3.2多继承
3.2.1定义
一个子类有两个及以上的直接父类,如:
class Assistant : public Student,public Teacher
{}
3.2.2菱形继承中的数据冗余和二义性
用例子来说明:
如图,Student与Teacher都继承自Person,而Assistant又多继承了Student与Teacher,那么这时候Student与Teacher中属于Person的那一部分就会出现冗余,我们称之为数据冗余。
当我们运行下面一段代码的时候
Assistant a;
a._name;
此时我们会发现在Assistant中有两份_name,一份属于Student,一份属于Teacher,因此运行时会因为不知访问那个而报错,我们称之为二义性。
3.2.3virtual:虚继承
按照之前分析,我们已经知道了数据冗余和二义性的存在,那么应该如何解决它呢?
为此,我们引入了virtual关键字来完成虚继承的概念,虚继承后,我们可以得到如下图的存储方式:
使用虚继承的方法也很简单,只需要在继承共有父类的时候加上virtual关键字:
class Student: virtual public Person
{}
3.2.3补:复杂菱形继承时的位置
假如要完成如下图的复杂菱形继承
分析可知,在继承A的时候会出现数据冗余和二义性的问题,因此我们要在
A->B
A->C
这两个继承的时候使用virtual来修饰
3.3总结
在实际应用当中,我么并不推荐使用菱形继承,因为这会降低代码可读性(虽然iostream就是菱形继承),但是使用多继承是完全没问题的
四、继承和组合
4.1两者的本质都是:复用
4.1补:黑盒测试和白盒测试的概念
白盒测试:可见底层代码的实现,是对代码功能的实现过程进行测试,对技术要求高
黑盒测试:不可见代码的底层实现,直接对代码的功能进行测试,对技术要求低
4.2is-a关系与has-a关系(特指类之间的关系)
public继承是一种is-a关系,就是说每个派生类对象都是一个基类对象
组合是一种has-a关系,假设B组合了A,每个B对象中都有一个A对象,例如我们在vector类模板中在成员变量列表使用了迭代器类来作为成员变量
4.2补:在程序设计中,我们推荐“高内聚,低耦合”的代码形式
其中低耦合特指两个类之间关系,我们希望类与类之间关系不要太紧密。
4.3组合的好处
组合的原则是在类中少放公用,大多在保护中实现,仅仅向外展示功能接口,因此组合十分符合低耦合的形式
4.4继承与组合的对比
①继承允许根据基类的实现来定义派生类的实现,这种以生成派生类为目的的复用,我们称为白箱复用,但是因为白箱可视度高,所以基类的改变对派生类影响很大,耦合度比较高
②对象组合是继承之外的另一种复用方式,class Person中定义保护成员hand h1;
而hand是另外实现的一个类,这时两个类之间体现出了对象组合
在这个例子中,我们称hand类被Person类组合了,被组合的对象通常需要有良好的接口,这种复用风格成为黑箱复用,因为对象内部不可见,耦合度低
4.5is-a与has-a关系的应用场景
has-a关系时用对象组合,例如人体和手
is-a关系时用继承,例如植物和水果
当has-a与is-a都符合的时候,用组合,如list与queue