标准C++ -- day06

bigflyny / 2023-08-28 / 原文

一、什么是继承
  1. 当遇到问题时,先查看现有的类是否能解决一部分问题,如果有则继承该类,并在此基础上扩展以此解决问题,从而缩短解决问题的时间(代码复用)
  2. 当遇到一个大而复杂的问题时,可以把大问题拆分成若干个不同的小问题,然后为每个小问题设计一个类来解决,最后通过继承的方式把这些类汇总到一个类中,从而解决大问题,以此降低问题的难度,可以同时让多个程序员共同解决大问题
派生类去继承基类 子类继承父类
二、继承的语法
  1. 继承表
class Son : // 继承表[继承方式 父类名1,继承方式 父类名2,...]
{
    //成员变量
public:
    //成员函数
}
  1. 继承方式

    • public

    • private

    • protected

三、C++继承的特点
  1. C++中的继承可以有多个父类
  2. 子类会继承父类的所有内容,是否能用另说
  3. 子类对象可以向它的父类类型转换(缩小),但是父类对象不能向子类类型转换(放大)
    Base* b = new Son;// true
    Base& b = son;    // true
    Son* s = new base;// false
    Son& s = base;    // false
    // 父类指针或引用可以指向子类对象,子类指针或引用不能指向父类对象
    
  4. 子类会隐藏父类的同名成员(成员变量、成员函数),不构成函数重载,因为作用域不同
    同名成员被隐藏后,在子类中直接访问到的是子类的同名成员
    但可以通过 父类名 :: 同名成员名 的方式指定访问父类同名成员
  5. 在执行子类构造函数的初始化列表时,会按照继承表的顺序来执行父类的构造函数,默认执行的是父类的无参构造,但也可以在子类的初始化列表中显式地调用父类的有参构造,然后再执行类类型成员的构造函数,最后执行子类的构造函数
    Son(int num){}                // 调用Base的无参构造
    Son(int num):Base(val)
    {}      // 调用Base的有参构造
    
  6. 在子类的析构函数执行结束后,再调用类类成员的析构函数,最后按照继承表的逆序调用父类的析构函数
  7. 当子类执行拷贝构造时,默认下只会调用父类的无参构造,但是这是有问题的,因此需要在子类的拷贝函数的初始化列表中显式地调用父类的拷贝构造函数
    Son(const Son& thar):Base(that) // 调用Base的拷贝构造
    {}    
    // 父类引用可以指向子类对象
    
  8. 当子类执行赋值函数时,默认下不会调用父类的赋值函数,如果需要调用父类的赋值函数,可以在子类的赋值操作中通过域限定符显式地调用父类的赋值操作
    Son& operator=(const Son& that)
    {
        Base::operator=(that); // 调用Base的赋值操作
    }
    
四、继承方式与访问控制属性
  1. 访问控制属性对成员的访问范围限制:

    public 可以在任意位置访问
    protected 只能在类内和子类中访问
    private 只能在类内访问

  2. 继承方式的影响
    • 父类的成员是否能直接在子类中访问,取决于父类中的访问控制属性,不受子类的继承方式影响

    • 子类的继承方式能决定父类成员被子类继承后,在子类中变成什么样的访问控制属性

    父类中的属性 public继承 protected继承 private继承
    public public protected private
    protected protected protected private
    private private private private
  3. 只有以public方式继承父类,父类的指针或引用才能指向子类对象,这也是多态的基础
五、多重继承和钻石(菱形)继承
  1. 多重继承
    • 当一个子类继承多个父类时,此时称为多重继承,会按照继承表的顺序在子类中排列父类的内容,当使用父类指针指向子类对象,编译器会自动计算出该父类的内容在子类中的位置,并让父类指针指向该位置,所有可能会出现使用同一个子类指针给不同的父类指针赋值后,地址编号不同的情况
  2. 钻石继承,菱形继承
    • 假设有一个类A,类B和类C都分别继承了类A,又有类D继承了类B、类C,一个类的父类有共同祖先时,形成了钻石(菱形)继承

    • 问题:

      • 类B、类C各自都有类A的内容

      • 类D会继承类B、类C的所有内容,就导致类D中继承了两份类A的内容

      • 当类D对象去访问类A的内容时,编译器不能确定访问的是哪份类A的内容,有歧义,会报错

  3. 虚继承
    • 使用 virtual关键字 去修饰继承表时,此时变成虚继承

    • 此时子类中会多出一个成员变量:虚指针,该指针会指向父类的内容,并且当该子类被继承时该虚指针会一起被继承,如果此时形成钻石继承时,孙子类中就会有多个指向相同位置的虚指针,此时编译器会比较这些虚指针指向的内容是否一致,如果是则只继承一份

    • 通过虚继承可以解决钻石继承中的访问共同祖先类内容会有歧义的问题