第四章 C++继承与派生

以下是本人整理的C++基础知识点,内容并不包含全面的C++知识,只是对C++重点内容、特点进行整理和归纳。

4.1 继承和派生的概念

继承和派生(两者表示一个概念)
    继承
        一个类获得另一个类的成员(变量和函数)

    派生
        一个类将自己的成员赋予另一个类(变量和函数)


父类和子类
    父类(基类)
        被继承的类

    子类(派生类)
        派生的类


继承的语法
    class 派生类名:[继承方式] 基类名{
        派生类新增加的成员
    };
    例子:class Student: public People{...};

4.2 C++三种继承方式

继承方式的介绍
    限定子类对父类成员的访问的最高权限
    三个关键字权限:public > protected > private,默认为 private
    一般使用 public 继承

public、protected、private 指定继承方式
    三种继承方式
        1、public继承的权限变化
            基类 public 成员 --> 派生类 public 成员
            基类 protected 成员 --> 派生类 protected 成员
            基类 private 成员 -->  派生类中不可见

        2、protected继承的权限变化
            基类 public 成员 --> 派生类 protected 成员
            基类 protected 成员 --> 派生类 protected 成员
            基类 private 成员 -->  派生类中不可见

        3、private 继承的权限变化
            基类 public 成员 --> 派生类 private 成员
            基类 protected 成员 --> 派生类 private 成员
            基类 private 成员 -->  派生类中不可见


    基类 private 成员在派生类中的特点
        被继承而且占用内存,但在派生类中不可见
        访问基类 private 成员的唯一方法:借助基类非 private 成员函数

    using 关键字:改变基类成员在派生类中的访问权限
        如:public --> private、protected --> public、protected --> public
        只能改变基类中 public 和 protected 成员的访问权限,private 成员不能改变 
        例子
            class Student : public People {
                public:
                    using People::m_name;  //原是protected,改为public

                private:
                    using People::show;  //原是public,改为private


            };

4.3 C++继承时的名字遮蔽问题

什么是名字遮蔽
    派生类成员和基类成员重名,派生类成员把基类成员遮蔽,实际使用的是派生类成员
    被遮蔽的基类成员仍然存在,可以通过类名和域解析符访问
        如:stu.People::show();


成员函数:名字遮蔽和函数重载
    基类和派生类的成员函数名字一样时会造成遮蔽,不论参数列表是否相同,不会产生函数重载
    同一个类内的函数才会构成重载;有继承关系的类之间只会产生名字遮蔽

4.4 C++类继承时的作用域嵌套

作用域包含
    类是一种作用域
    基类包含派生类
    Base > Derived

内层作用域和外层作用域
    内层作用域:派生类作用域
    外层作用域:基类作用域

编译器对类的名字查找顺序
    成员变量
        先在派生类中查找,如果找不到就到基类中查找

    成员函数
        先在派生类中查找,如果找不到就到基类中查找
        如果在派生类中找到了,但是函数参数不一致,报错

4.5 C++继承时的对象内存模型

类继承时的内存模型
    成员变量
        位置:堆区或栈区
        顺序:基类成员在前,派生类在后(基类成员在低内存地址)

    成员函数
        位置:代码区


成员变量遮蔽时的内存分布
    被遮蔽的所有基类成员变量都在内存中
    基类成员在前,派生类在后,顺序不变

4.6 C++基类和派生类的构造函数

类的构造函数不能被继承
    基类的构造函数既不能变成派生类的构造函数,也不能变成普通成员函数

基类成员的初始化
    初始化方法
        在派生类构造函数的初始化列表中,显式指明调用基类的构造函数
            解决基类private成员无法初始化的问题

        不显式指明,调用基类默认构造函数
            没有默认构造函数,编译失败


    派生类构造函数对成员变量的初始化顺序
        先调用基类构造函数,再执行其他代码
        最外层的基类构造函数最先被调用

    派生类构造函数的执行顺序
        从最外层基类到派生类
        派生类构造函数只能调用直接基类的构造函数,不能调用间接基类的
            虚继承的基类构造函数除外


    初始化例子
        class Student: public People{...}
        Student::Student(char *name, int age, float score): People(name, age), m_score(score){ }

4.7 C++基类和派生类的析构函数

析构函数和构造函数一样,不能被继承
析构函数的执行顺序
    先执行派生类析构函数,再执行基类析构函数
    跟构造函数的执行顺序相反

4.8 C++多继承

多继承介绍
    一个派生类拥有至少2个基类
    多继承容易让代码逻辑复杂、思路混乱,较少使用

多继承语法
    class D: public A, private B, protected C{
       //类D新增加的成员
    }

多继承下的构造函数
    在派生类构造函数的初始化列表中,调用多个基类的构造函数
    基类构造函数的调用顺序:和声明派生类时基类出现的顺序相同
    写法:D(形参列表): A(实参列表), B(实参列表), C(实参列表){...}

多继承下的命名冲突
    发生冲突时,使用类名和域解析符::访问成员
    BaseA::show();

4.9 C++多继承时的对象内存模型

Base A、Base B 、Derived C
class C: public A, public B{...}
内存分布(从低到高):A、B 、C,也就是和声明顺序相同

4.10 借助指针访问private、protected属性的成员变量

对象的起始地址 + 偏移

4.11 C++虚继承

多继承的冲突:菱形继承
    派生类中保留间接基类的多份同名成员,一般是多余的
    访问时会产生歧义和冲突,不知道通过那个继承路径访问

虚继承
    虚继承介绍
        作用:存在重复继承的间接类时,派生类中只保留一份间接基类的成员,解决多继承时的命名冲突和冗余数据
        目的:在继承关系中,让某个类和其它类共享它的基类。其中,这个被共享的基类就称为虚基类

    虚继承语法
        class A{...}
        class B: virtual public A{...}
        class C: virtual public A{...}
        class D: public B, public C{...}

4.12 C++虚继承时的构造函数

虚继承时的构造函数
    存在虚继承时,派生类的构造函数必须显式调用虚基类的构造函数
    例子
        虚继承【 D::D(...): A(), B(), C(), m_d(){... }  】
            A为D的间接基类,被B、C虚继承

        一般继承【 D::D(...): B(), C(), m_d(){... }  】 
            A为D的间接基类,被B、C继承



虚继承时构造函数的执行顺序
    先调用虚基类的构造函数
    再根据声明顺序,调用其它直接基类的构造函数

4.13 C++虚继承下的 内存模型

内存特点
    无论是虚基类的直接派生类还是基类的间接派生类,虚基类的子对象始终位于派生类对象的最后面,也就是位于高地址
    虚基类越底层,内存地址越高

编译器对虚继承对象内存的规则
    内存分布
        分为固定部分和共享部分
            固定部分:非虚继承部分
            共享部分:虚继承部分

        固定部分放在低位,共享部分放在高位

    虚继承成员变量的计算
        不同编译器没有统一标准
        VC编译器解决方案
            引入了虚基类表,用一个指针指向虚基类
            虚基类表是一个数组,元素存放的是各个虚基类相对于首地址的偏移。

4.14 C++将派生类赋值给基类(向上转型)

数据类型的转换
    基本数据类型
        编译器知道如何对数据进行取舍

    “类”数据类型
        只有在基类和派生类之间才有意义
        只能将派生类赋值给基类(向上转型)
            派生类对象赋值给基类对象
            派生类指针赋值给基类指针
            派生类引用赋值给基类引用



向上转型和向下转型
    向上转型:将派生类赋值给基类,安全
    向下转型:将基类赋值给派生类,有风险
        编译器不知道如何填充剩下的内存


将派生类对象赋值给基类对象
    对象之间的赋值 --> 成员变量的赋值
    派生类专属的成员丢弃
    不影响成员函数和 this 指针

将派生类指针赋值给基类指针
    特点
        仅仅是改变了指针的指向
        对象的指针必须要指向对象的起始位置

    通过基类指针访问派生类的成员
        访问派生类的成员变量
            能够访问派生类中来自基类的成员变量
                基类指针指向派生类的对象,使得隐式指针 this 也指向了 派生类的对象,最终基类指针使用的是派生类对象的成员变量
                编译器通过指针来访问成员变量,指向哪个对象就使用哪个对象的数据


        访问派生类的成员函数
            不能访问派生类的成员函数,只能访问基类的成员函数
                编译器通过指针的类型来访问成员函数,指针属于哪个类的类型就使用哪个类的函数

将派生类引用赋值给基类引用
    和指针一致
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值