继承的概念
继承源于生物界,指后代能够传承前代的特征和行为。
基类和子类
继承使我们可以通过一个已有的类创建一个新类,新类自然获得已有类的全部功能。在某个继承关系中,已有的类在这个继承关系中作为基类、超类或父类,新类称为派生类或子类。
具体来讲,子类复制了基类的全体数据成员和成员函数。此外,子类还可以进行扩充、修改和重定义。
同一个类可以作为多个类的基类,一个子类也可以作为另一个类的基类。
基类和子类是继承关系中的概念,并不是本质的概念,而是关系上的概念。
继承与派生
A作为基类,B作为子类时,我们称:
- B继承A
- A派生B
单继承和多继承
只能从一个基类派生的继承称为单继承,即一个类一次最多继承一个基类。
可以从多个基类派生的继承称为多继承。
C++支持单继承和多继承。
Java只支持单继承,需要“多继承”时使用接口实现。(接口是什么,学了Java就知道了)
类成员访问权限
C++类成员访问权限默认为private。
public
语法正确的条件下,访问不受控制,在哪里都可以访问。
private
只能在类内、友元函数访问。
protected
只能在类内、子类内、友元函数访问。
继承方式
注意:继承方式决定的是子类如何继承基类成员的访问权限。
不同继承方式会不同程度地改变基类成员在派生类中的访问权限。(个人觉得这句话不好,易让人误解)
C++中继承方式默认为private
。
public
基类成员的访问权限在派生类中保持不变。
基类的private
成员、public
成员、protected
成员在子类中保持他们在基类中相同的访问权限。
private
基类成员的访问权限在派生类中全部变为private
。
protected
基类的public
成员的访问权限在派生类中变为protected
。
private
成员和protected
成员的访问权限不变。
派生类对基类的扩展
派生类可以:
- 增加新的数据成员和成员函数
- 重载从基类继承到的成员函数
- 覆盖(重定义)从基类集成到的成员函数
- 改变基类成员在派生类中的访问属性(通过继承方式实现)
派生类不能继承基类的以下内容:
- 析构函数
- 基类的友元函数
- 静态成员
- 静态数据成员
- 静态成员函数
注意:
静态成员为整个继承体系公有。
C++11之前,派生类不能继承基类的构造函数,C++11之后可以,通过using
实现。
成员函数的重定义、重载及隐藏
派生类对继承到的基类成员函数的重定义或重载都会影响它们在派生类中的可见性。
派生类对基类函数进行重定义或重载时,由于继承和类作用域的原因,导致了隐藏,解决隐藏有三种方法。
影响可见性:
如果派生类重定义或者重载了基类中某个函数名为func
的函数,则基类中的所有名字为func
的函数都会被隐藏。(其中隐藏指的是在派生类中不能直接看到这个函数。)
解决:
通过基类名称访问被派生类重定义或重载隐藏的函数,
如
基类::函数名(...)
。重载基类的所有同名函数,这些重载函数的代码与基类完全相同。
使用
using
。
前两种方法都很繁琐。
而C++11提供了using
使基类中被隐藏的函数在派生类中可见,具体使用在后边的using
部分讲解。
重定义
是什么
覆盖也称为重定义、重写。
重定义是指派生类定义与基类具有相同参数列表的同名成员函数。
(关于重定义的定义,我个人认为书上P147的说法是错的。)
隐藏及解决
代码如下:
1 |
|
将第34行注释后,输出结果如下:
1 | Base::print() |
重载
是什么
重载与重定义不同,重载要求成员函数名称相同,但具有不同的参数列表。
(关于重载的定义,我个人认为书上P147的说法是错的)
隐藏及解决
1 |
|
将第33行注释后,输出结果如下:
1 | Base::print() |
using
using有三个用途:
- 使隐藏的函数重现
- 改变基类成员在派生类中的访问权限
- 使子类继承基类构造函数(在继承第二讲中讲解如何使用)
使隐藏函数重现
方法:在派生类中用using 基类::函数名;
声明基类的函数名,不需提供函数参数。
一条using
可以把指定函数名的所有版本添加到派生类作用域中,它们的访问权限与using语句所在区域的访问权限相同。
1 |
|
改变基类成员在派生类中的访问权限
在派生类的public
、protected
或private
权限区域内,使用using
再次声明基类的非private
成员,就可以重新设置它们在派生类中的权限为using
语句所在区域的权限。
继承和类作用域
在存在继承关系时,派生类的作用域嵌套在基类作用域的内层。
因此,在解析类成员名称时,如果在本类的作用域内没有找到,编译器会接着在外层的基类作用域内继续寻找该成员名称的定义。
在这个寻找的过程中,一旦在某个作用域找到了,就停止查找,即使外层作用域内还有同名成员,也不找了。
形式如下:
1 | Base { |
代码验证:
1 |
|
作者:@臭咸鱼
转载请注明出处:https://chouxianyu.github.io
欢迎讨论和交流!