• C/C++基础知识总结——继承与派生


    1. 类的继承与派生

    1.1 派生类的定义

      (1) 定义规范

        class 派生类名: 继承方式 基类1名, 继承方式 基类2名...

        {

           ...派生类成员声明;

        };

      (2) 从以上形式上看可以多继承

      (3) 继承方式规定了如何访问从基类继承的成员

      (4) 继承的主要目的是实现代码的重用与扩充

      (5) 派生类生成过程

        ① 吸收基类成员

          A. 继承后,派生类包含了基类中除构造和析构函数之外的所有成员。构造和析构函数需要重新定义

        ② 改造基类成员

          A. 通过继承方式来控制对基类成员的访问控制

          B. 对基类的数据或函数成员进行覆盖和隐藏。隐藏的意思是在派生类中定义一个与基类函数同名(同参数)的函数。(不同参数叫重载)

        ③ 添加新的成员

    2. 访问控制

    2.1 公有继承 public

      (1) 基类的公有成员和保护成员的访问属性在派生类中不变,基类的私有成员不可直接访问

    2.2 私有继承 private

      (1) 默认继承方式为私有继承

      (2) 基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可直接访问

        也就是说基类成员都以私有方式存在派生类中,派生类中其他成员可以访问这些。但是在类外无法通过派生类对象访问这些。

        经过私有继承后,所有基类成员都成为派生类的私有成员,如果进一步派生的话,基类的全部成员都无法在新的派生类中被直接访问。因此私有继承用的比较少。

    2.3 保护继承 protected

      (1) 基类的公有和保护成员都以保护成员身份出现在派生类中,基类的私有成员不可继承。

      (2) 注意私有继承和保护继承的区别

    2.4 静态成员不受继承方式影响,在整个类层次体系中都可以访问

    3. 类型兼容规则

    3.1 定义:

      在需要基类对象的时候,都可以用公有派生类的对象来替代

    3.2 类型兼容规则所指的替代包括以下情况。替代后,派生类对象就可以作为基类对象使用,但只能使用从基类继承的成员。

      (1) 派生类的对象可以隐含转换为基类对象

      (2) 派生类的对象可以初始化基类的引用

      (3) 派生类的对象可以隐含转换为基类的指针

      例子:

    class B{...}
    class D: public B{...}
    B b1, *pb1;
    D d1;
    b1 = d1; //实现(1)
    B &rb = d1;//实现(2)
    pb1 = &d1;//实现(3)

    4. 派生类的构造与析构函数

    4.1 构造函数

      (1) 构造派生类对象时,需要对基类的成员对象和新增成员对象进行初始化。基类的构造函数并没有继承下来,因此需要给派生类添加新的构造函数。派生类对于基类的很多成员对象不能访问,因此需要调用基类的构造函数来对基类中不能访问的成员进行初始化。在构造派生类对象时,首先调用基类的构造函数,然后构造初始化参数表,然后执行函数体。

      (2) 语法:

      派生类名::派生类名(参数表):

      基类名1(基类1初始化参数表)...,基类名n(基类n初始化参数表),

      成员对象名1(成员对象名1初始化参数表),...,成员对象m(成员对象m参数表)

      {

         ...其他初始化工作

      }

      (3) 派生类构造函数执行一般次序如下

        ① 调用基类构造函数,按照继承时声明顺序

        ② 对派生类新增的成员对象初始化,按照类中声明顺序

        ③ 执行构造函数体

    4.2 复制构造函数

    4.3 析构函数

      (1) 执行顺序与构造函数相反。

        ① 先执行析构函数体

        ② 再调用派生类中类对象的析构函数体

        ③ 调用基类的析构函数

    5. 派生类成员的标识与访问

    5.1 作用域分辨符 ::

      (1) 如果派生类声明了与基类成员函数同名的新函数,即使函数参数不同,从基类中继承的同名函数的所有重载形式都会被隐藏。只有在相同作用域中定义的函数才可以重载。。。(这个地方???)

      (2) 如果某个派生类继承了多个基类,在这些基类中有同名的数据。此时访问方式为:

    class base1
    {
        int a;
    }
    class base2
    {
        int a;
    }
    class derived: public base1, public base2{
    }
    derived d;
    d.base1::var = 2;//以这种方式!切记!
    d.base2::var = 4;

      (3) 还是一个派生类derived的两个父类base1、base2是由同一个基类base0派生的,那么,base1和base2中都有base0的一个副本,而只想要一个副本。那就需要用到虚基类。

    5.2 虚基类

      (1) 如果将上面讲的base0设为虚基类,则不同路径继承到derived的同名数据在内存中只有一个副本,同一个函数名只有一个映射。这样就解决了同名成员唯一标识问题。

      (2) 语法:

      class 派生类名:virtual 继承方式 基类名

      (3) 例子:

    class base0
    {
        int var0;
    }
    class base1:virtual public base0
    {
    }
    class base2:virtual public base0
    {
    }
    class derived: public base1, public base2{
    }
    derived d;
    d.var0 = 0;//只有一个var0的副本

    5.3 虚基类及派生类的构造函数

      (1) 如果上面的例子中虚基类没有默认构造函数就比较麻烦了,要改成下面的形式

    class base0{
        base0(int var):var0(var){}
        int var0;
    };
    class base1: virtual public base0
    {
        base1(int var):base0(var){}
    }
    class base2:virtual public base0
    {
        base2(int bar):base0(var){}
    }
    class derived:public base1, public base2
    {
       derived(int var):base0(var),base1(var),base2(var){};
    }

      (2) 虽然已经改造了,但是不用担心base0的var0会初始化三次,因为在创建derived的对象时,首先调用原始基类的构造函数,之后的base1和base2中对base0的构造函数都被自动忽略了。

      (3) 构造一个类对象的一般顺序为:

        ① 如果该类有直接或间接的虚基类,执行虚基类的构造函数

        ② 如果还有其他基类,按照继承声明中的顺序,执行构造函数,但不执行虚基类的构造函数

        ③ 按照类定义中的顺序,对派生类的成员对象进行初始化。对于类对象成员,如果出现在构造函数初始化列表中,则以指定的参数执行构造函数,如未出      现,则执行默认构造函数;对于基本数据类型,如果出现在构造函数初始化列表中,则用指定的值初始化,否则什么也不做

        ④ 执行构造函数的函数体

     

    作者:viczzx 出处:http://www.cnblogs.com/zixuan-zhang 欢迎转载,也请保留这段声明。谢谢!

  • 相关阅读:
    研发环境容器化实施过程(docker + docker-compose + jenkins)
    Java虚拟机-字节码执行引擎
    Java虚拟机-类加载机制
    Java虚拟机-字节码指令
    Java虚拟机-类文件结构
    Java虚拟机理解-内存管理
    单元测试实践(SpringCloud+Junit5+Mockito+DataMocker)
    Git基础概念与Flow流程介绍
    谷歌最佳实践
    谷歌最佳实践
  • 原文地址:https://www.cnblogs.com/zixuan-zhang/p/3335023.html
Copyright © 2020-2023  润新知