文章目录
- 继承的定义
- 继承的语法
- 继承权限和继承到子类后父类成员的访问限定符的变化
- 继承到子类后父类成员的访问限定符的变化
- 子类继承到了父类的什么?
- 继承中的作用域
- 子类和父类之间的赋值转换
- 子类对象可以直接赋值给父类对象,但是父类对象不能直接赋值给子类对象
- 父类指针可以直接指向 子类 对象,但是子类指针不能直接指向父类对象
- 父类类型的引用可以直接引用 子类 对象,但是子类引用不能直接引用父类对象
继承的定义
继承(inheritance)机制是面向对象程序设计使代码复用的最重要的手段
它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类(或者子类)。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
继承的目的是为了代码重用
、扩展基类的行为
,并建立一个类型之间的层次结构。
继承的语法
C++的继承分为单继承和多继承
单继承语法:
class 子类名 :继承权限 父类名
{
子类类体
}
多继承语法:
class 子类名: 继承权限 父类名,继承权限 父类名,继承权限 父类名, ......
{
子类类体
}
多继承的语法其实就是在单继承后面加上逗号,再进行添加继承的父类
继承权限和继承到子类后父类成员的访问限定符的变化
3个继承权限和类里面的3个访问限定符一样,分为:
- 公有继承权限:关键字
public
- 保护继承权限:关键字
protected
- 私有继承权限:关键字
private
继承到子类后父类成员的访问限定符的变化
继承到子类后父类成员的访问限定符的变化都是固定的
,语法规定好的
,所以记住就好
可以表现成如下的表格
类成员/继承方式 | public 继承 | protected 继承 | private 继承 |
---|---|---|---|
父类中的 public 成员 | 变成子类中的public 成员 | 变成子类中的protected 成员 | 变成子类中的private 成员 |
父类中的 protected 成员 | 变成子类中的protected 成员 | 变成子类中的protected 成员 | 变成子类中的private 成员 |
父类中的 private 成员 | 子类不可访问 | 子类不可访问 | 子类不可访问 |
注意:
-
父类中的
private
限定的成员,不管以什么继承权限继承到子类,子类都访问不了
因为这是private
限定符的特点:除了友元以外,private
限定的都只能
在类域里面被访问,类外的任何地方都不能访问 -
父类中被
protected
限定符限定的成员,可以在继承该父类的子类
以及父类自己
的类域里面访问,在其他地方不能访问
所以protected
限定符是专门为继承而设置的 -
使用关键字class定义的子类默认的继承方式是
private
使用struct定义的子类则默认的继承方式是public
不过为了可读性,最好显示的写出继承方式 -
虽然继承权限有3个,但是我们实际写代码用继承的时候,基本上都会用
public
继承权限进行继承
子类继承到了父类的什么?
子类会继承父类的所有成员【包括成员变量和成员函数
】,父类的private修饰的私有成员也会继承下来,只不过就算继承下来了,子类也访问不了
但是其中又有一些小细节:
-
子类继承来的
非静态
成员变量,子类实例化对象后会把它们存储在自己的对象中。
但是静态
成员变量就不会
,而且子类继承到的静态成员变量和父类中的是同一个,同一地址
的变量
因为父类自己的静态成员变量都不存储在对象中,而是存储在全局区
,让所有的父类对象访问到的是同一变量
所以其是子类继承静态成员变量,就相当于是子类的所有对象和父类所有对象一样,拥有了访问存储在全局区的父类的静态成员变量的权利 -
因为
所有类
的成员函数都不存储在对象中,而是和普通函数一样存储在公共的代码段
,只不过有类域的限制
所以子类继承父类的成员函数当然不是拷贝一份下来
,而是和父类一样拥有了访问和使用这些函数的权利了。
继承中的作用域
-
子类和父类的作用域是
不同的,独立的
,是不同的两个类域
所以子类才不能
在自己的类域中访问父类的private成员,同样的父类也不能
在自己的类域中访问子类定义的private成员 -
如果子类自己又定义了与父类同名的成员【
包括成员变量和成员函数
】,那么子类自己定义的
和从父类那里继承来的
同名的成员就构成隐藏
隐藏:构成隐藏的成员虽然同名,并且在同一作用域【子类类域
】下,但是不会报错,可以共存
但是子类对象(指针,引用)直接使用
这个构成隐藏的成员的时候,只会使用到子类自己定义
的那个成员。
此时如果要使用从父类那里继承来的,就要加上父类的类域
进行限制
例
此时要特别注意:
因为子类的自己定义的成员函数与从父类那里继承来的成员函数同名,也会构成隐藏。
也就意味着:此时子类调用直接构成隐藏成员函数的时候,只会找子类自己类域中的同名函数
,不会
去父类里面找,即使父类里面有合适的
因为有函数重载的存在,此时使用不慎就会出错
例
因为子类直接调用构成隐藏的成员函数,就只会在子类自己的类域中找,同名就构成隐藏了
但是因为函数重载
的存在,编译器对函数调用的要求更高,要同名同参数表才能调用
所以因为构成隐藏就只能在子类中找函数进行调用,但是在子类里面又找不到调用的func的重载,那就只能报错了
所以实际的代码编写的时候,尽量不要定义同名的成员
子类和父类之间的赋值转换
子类对象可以直接赋值给父类对象,但是父类对象不能直接赋值给子类对象
子类对象可以直接赋值给父类对象是因为:
-
子类会继承父类的所有成员,所以
父类里面有的成员,子类里面也一定有
-
即使子类自己又新增了成员变量,子类对象赋值给父类对象的时候也只需要“切割”一下就好
此时,子类B的对象要赋值给父类A的对象,也只需要沿着上图中蓝色的线“切割”一下就好
而父类对象之所以不能直接赋值给子类对象
就是因为如果子类对象新增了成员,那新增的成员父类一定是没有的
如果赋值过去的话,子类自己新增的成员就接收不到赋值
父类指针可以直接指向 子类 对象,但是子类指针不能直接指向父类对象
父类指针可以直接指向子类对象,是因为:
- 父类对象的大小一定是
小于等于
子类对象的大小的,所以父类指针指向子类对象的时候,解引用父类指针一定不会越界访问
- 父类指针指向子类对象的时候,父类指针只能操纵子类从父类那里继承来的成员
例
所以父类指针可以直接指向子类对象,并且是安全的
子类指针不能直接指向父类对象,就是因为:
子类对象的大小一定大于等于
父类对象,那么当子类对象的大小大于父类对象时,子类指针指向父类对象的话
解引用一下子类指针,或者访问(修改)一下子类自己新增的成员,都会越界
例
所以子类指针直接指父类对象是非常不安全的
父类类型的引用可以直接引用 子类 对象,但是子类引用不能直接引用父类对象
原因和父类指针可以直接指向 子类 对象,但是子类指针不能直接指向父类对象的原因一模一样
:
- 父类对象的大小一定是
小于等于
子类对象的大小的,所以父类引用引用子类对象的时候,直接使用父类引用一定不会越界访问
- 父类引用引用子类对象的时候,父类引用也只能操纵子类从父类那里继承来的成员
- 子类对象的大小一定
大于等于
父类对象,那么当子类对象的大小大于父类对象时,子类引用引用父类对象的话
访问(修改)一下子类自己新增的成员
,就会越界
例