一,概念及用法
1)概念
首先我们来了解一下官方的概念:继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
官方定义说了一堆话,可能我们还没了解它的大概意思,现在我来帮助大家理解一下。首先抓住关键词——复用,继承的功能就是复用(使用已经写好的代码)。为什么要有继承呢?想象一个场景,我们要写一个教职工系统,学校有那么多类人,有老师,教务处处长,校长,辅导员,保安……,如果我们把他们都当作一个类,有很多的重复代码,比如年龄,性别,身高等等,有多少种类我们就要写多少份,我们能不能把他们提取出来,只需要一份,通过某种手段将他传递,这种手段就是继承。我们首先把所有职位的共同点提取出来,他们都是人,我们把人单独写一个类,然后继承给其他人,达到复用的效果。
2)用法
在子类(继承其他类的类称为子类)的名称后面先写“:”。然后再写继承其他类的方式——私有,公有,保护,再加父类(被继承的类或者想要继承的类)。
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; // 学号 };
那就有人要问了,继承方式有啥用?我们先看一张常用的表。(继承只继承父类中的公有成员,保护成员,不继承私有成员)
私有继承是自私的,自己继承了之后就不允许它的子类继承父类,也不允许外界直接访问,将父类的成员访问权限全改为私有。保护继承允许自己的子类继承,但是不允许外界访问,将父类中的公有成员访问属性改为保护。公有继承,允许外界直接访问父类的公有成员,不改变父类任何成员的访问属性。
3)构造函数和析构函数问题
在子类中如果我们不显示的调用父类的构造函数,会调用父类的无参构造函数,如果父类没有无参构造函数而导致报错,有参数则可以在初始化列表中调用,不建议在子类的构造函数中调用,因为它可能导致重复调用父类的构造函数,或者在某些情况下,可能根本不应该在子类构造函数体内部调用父类的构造函数。析构函数一般不需要我们管,系统默认调用就行了。
4)特殊情况
在继承的时候如果出现了,子类中的成员和父类中的成员命名一样怎么办,如果不指定类名,一般是子类优先,那我们想要用父类中的东西怎么办呢?我们可以使用父类名+::的方法访问,例如:
Person::add(); studengt::add();
这样就可以访问父类的成员变量和函数。
二,虚继承及原理
现在有一个场景如图
在Assistant类中是不是有两个person类,很明显我们不需要两个person类,这样子无疑浪费了很多的空间,我们应该怎么解决呢?C++给出了一直解决方法,虚拟继承。
1)虚拟继承用法
在public前面加上virtual,以前面的例子为例
但是这个virtual加在哪里又是一个问题,我们应该加在哪里呢?首先我们要知道哪个类是重复的多余的,然后再找到这重复类的直接子类,注意是直接子类(重复类的亲儿子)的继承方式前面加virtual。
2)原理
虚拟继承是如何实现的呢?其实很简单,就是判断有多个类的时候,只在公共区创建一个就行了。现在又出现了一个问题,构造函数会不会重复调用(因为有最原始的父类有两个类能直接调用它的构造函数,即上例person),但代码只有一份,这如果不处理好会直接报错,特别是带参的,这个时候我们可以在最子类(即上例的Assistant)显示的调用person的构造函数,这样子系统识别到虚拟继承就不会走其他类中包含的重复的类的构造函数,问题就这样完美解决了。