继承与派生

书写格式

1
2
3
4
class  派生类名:继承方式  基类名 
{
//派生类新增的数据成员和成员函数
};

基类成员在派生类中的访问属性

基类中访问属性 继承方式 派生类中访问属性
private public 不可直接访问
private private 不可直接访问
private protected 不可直接访问
public public public
public private private
public protected protected
protected public protected
protected private private
protected protected protected

*inaccessible>private>protected>public*

基类的private成员在私有派生类中是不可直接访问的,所以无论是派生类成员还是通过派生类的对象,都无法直接访问从基类继承来的private成员,但是可以通过基类提供的public成员函数间接访问。

派生类对象当作基类使用

  • 派生的对象可以赋给基类的对象。

  • 派生类的对象可以初始化基类的引用

    1
    2
    derive d;
    base &br=d;
  • 派生类的对象的地址可以赋给指向基类的指针

    1
    2
    derived d;
    base *pb=&d;
  • 上述三种情形中,在后两种情况下,通过指针或引用只能访问对象d中所继承的基类成员。

多继承

1
2
3
class 派生类名:继承方式1  基类名1,…,继承方式n  基类名n{
// 派生类新增的数据成员和成员函数
};

不写继承方式则默认private继承

虚基类

声明:定义派生类时声明

1
2
3
4
5
6
7
class  派生类名:virtual  继承方式  类名{
//…
}
//下面的写法也可
class 派生类名:继承方式 virtual 类名{
//…
}

派生类的构造与析构

基类的构造函数和析构函数不能被继承,一般派生类要加入自己的构造函数。

  • 派生类含有内嵌对象成员时,构造函数形式:

    1
    2
    3
    4
    派生类名(参数总表):基类名(参数表1),内嵌对象名1(内嵌对象参数表1),…,内嵌对象名n(内嵌对象参数表n)
    {
    // 派生类新增成员的初始化语句
    }
    • 在定义派生类对象时,构造函数的执行顺序如下:

      • 调用基类的构造函数;
      • 调用内嵌对象成员(子对象类)的构造函数(有多个对象成员时,调用顺序由它们在类中声明的顺序确定);
      • 派生类的构造函数体中的内容

      撤消对象时,析构函数的调用顺序与构造函数的调用顺序正好相反。

  • 注意:

    • 当基类构造函数不带参数时,派生类可不定义构造函数,但基类构造函数带有参数,则派生类必须定义构造函数。
    • 若基类使用缺省构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去“:基类构造函数名(参数表)”
    • 基类和派生类的析构函数是各自独立的。
  • 多继承的构造函数

    1
    2
    3
    4
    派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n)
    {
    // 派生类新增成员的初始化语句
    }
    • 构造函数执行顺序:先执行基类构造函数,再执行对象成员的构造函数,最后执行派生类构造函数。
      • 处于同一层次各基类构造函数执行顺序,取决于声明派生类时所指定各基类的顺序,与派生类构造函数中所定义的成员初始化列表的各项顺序无关。
  • 虚基类的初始化

    • 建立一个对象时,如果这个对象中含有从虚基类继承来的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的。该派生类的其他基类对虚基类构造函数的调用都自动被忽略。
    • 若同一层次中同时包含虚基类和非虚基类,应先调用虚基类的构造函数,再调用非虚基类的构造函数,最后调用派生类构造函数;
    • 对于多个虚基类,构造函数的执行顺序仍然是先左后右,自上而下;
    • C++规定,虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。如果一个派生类有一个直接或间接的虚基类,那么派生类的构造函数的成员初始列表中必须列出对虚基类构造函数的调用,如果未被列出,则表示使用该虚基类的缺省构造函数来初始化派生类对象中的虚基类子对象。

赋值兼容原则

可以把指向派生类对象的指针赋值给指向基类对象的指针。例如:

1
2
Derived *dptr;
Base *bptr=dptr;
  • 注意

    • 声明为指向基类对象的指针可以指向它的公有派生的对象,但不允许指向它的私有派生的对象。

    • 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类中定义的成员。

    • 若想访问其公有派生类的特定成员,可以将基类指针用显示类型转换为派生类指针。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      class  A {
      //…
      public
      void print1();
      };
      class Bpublic A {
      //…
      public
      print2();
      };
      void main()
      {
      A op1,*ptr; // 定义基类A的对象op1和基类指针ptr
      B op2; // 定义派生类B的对象op2
      ptr=&op1; // 将指针ptr指向基类对象op1
      ptr->print1(); // 调用基类函数print1()
      ptr=&op2; // 将指针ptr指向派生类对象op2
      ptr->print1(); // 调用对象op2从其基类继承来的成员函数print1()
      ptr->print2(); // 错误,基类指针ptr不能访问派生类中定义
      //的成员函数print2()
      //应当改为:
      ((B*)ptr)-> print2();