高级程序设计

15 - 继承的基本概念和单继承

2020-03-26 14:00 CST
2020-03-26 11:37 CST
CC BY-NC 4.0

复习:如果面向对象程序设计没有继承,则被称为基于对象的程序设计。

软件复用:在开发新软件时,把现有的软件或软件的一部分拿过来用称为软件复用。

继承的基本概念

继承机制(Inheritance):对一个面向对象的程序,在定义一个新的类时,先把已有程序中的一个或多个类的功能全部包含进来,然后在新的类中再给出新功能的定义或对已有类的某些功能重新定义。

基类和派生类

在继承关系中存在两个类:基类(或称父类)和派生类(或称子类)。

派生类拥有基类的所有成员,并可以定义新的成员或对基类的一些成员(成员函数)进行重定义。

继承分为单继承(一个类只有一个直接基类)和多继承(一个类有多个直接基类)。

继承对程序设计的支持

继承机制除了支持软件复用外,它还具有下面的作用:

  • 对处理的对象按层次进行分类
  • 对概念进行组合
  • 支持软件的增量开发(版本升级)

单继承

在定义单继承时,派生类只能有一个直接基类,其定义格式如下:

    class <派生类名>: [<继承方式>]<基类名> {
        <成员说明表>
    };

成员说明表是在派生类中新定义的成员,其中包括对基类成员的重定义;继承方式用于指出从基类继承而来的成员在派生类中对外的访问控制。

派生类除了拥有新定义的成员外,还拥有基类的所有成员(基类的构造函数、赋值操作符重载函数、析构函数除外)。

定义派生类时一定要已经见到基类的定义,否则编译器无法确定继承的成员所需内存空间的大小、成员函数的类型。

如果在派生类中没有显式说明,基类的友元不是派生类的友元;如果基类是另一个类的友元,而该类没有显式说明,则派生类也不是该类的友元。

在派生类中访问基类成员

C++中,派生类不能直接访问基类的私有成员。

在派生类中定义新的成员函数或对基类已有函数重定义时,往往需要直接访问基类的一些private成员(特别是数据成员),否则新的功能无法实现;但private成员不允许外界使用(数据封装),导致了继承与封装的矛盾。

访问控制:protected

在C++中提供了另外一种类成员访问控制:protected

  • protected说明的成员不能通过对象使用,但可以在派生类中使用;
  • protected访问控制缓解了封装与继承的矛盾。

C++类向外界提供了两种接口:

  • public:对象的使用者(类的实例用户)
  • public + protected:派生类

派生类成员标识符的作用域

一个定义了的标识符的有效范围(能被访问的程序段)称为该标识符的作用域。

复习:C++标识符的作用域:

  • 局部作用域
  • 全局作用域
  • 文件作用域
  • 函数作用域
  • 函数原型作用域
  • 类作用域
  • 名空间作用域

派生类对基类成员的访问除了受到基类的访问控制的限制外,还要受到标识符作用域的限制。

类作用域:

  • 在类中定义的标识符局部于类定义
  • 在类中任何地方都可以访问
  • 在类外使用类中定义的标识符时,需要通过对象名受限或者类名受限
  • 在类中使用与成员标识符同名的全局标识符时,需要在全局标识符前面加上全局域解析符::

对基类而言,派生类成员标识符的作用域是嵌套在基类作用域中的。(派生类可以访问基类的标识符)

如果派生类中定义了与基类同名的成员,则基类成员名在派生类的作用域内不直接可见(被隐藏)。访问基类同名成员时要用基类名受限,如:

class B: class A {
    public:
        void f();
        void g() {
          f();    // B类的f
          A::f(); // A类的f
        }
};
B b;
b.f();    // B类的f
b.A::f(); // A类的f

即使派生类中定义了与基类同名但参数不同的成员函数,基类的同名函数在派生类中的作用域也是不直接可见的,可以用基类名受限来访问。

对于上面两种情况,也可以在派生类中使用using声明把基类中某个函数的函数名对派生类开放,如:。

class B: class A {
    public:
        using A::f;
        void g() {
          f(); // A类的f
        }
};

继承方式

有了继承机制后,一个类的成员有两种被外界使用的场合:

  • 通过类的对象(实例)使用
  • 在派生类中使用

基类的成员会变成派生类的什么成员是由继承方式指定的,默认的继承方式是private

继承方式 private protected public
private 不可直接访问 private private
protected 不可直接访问 protected protected
public 不可直接访问 protected public