• C++继承(1)


    1. 继承的概念及定义

    继承:利用已有的数据类型来定义新的数据类型。

    通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。 我们称已存在的用来继承的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。

    继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。

    class Person//父类
    {};
    class Student : public Person//子类继承
    {};
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了Student和Teacher复用了Person的成员。

    2. 继承关系和访问限定符

    继承方式和访问方式都有三种:
    在这里插入图片描述

    • 注意的点

    所谓的继承方式和访问限定符,只影响父类继承下来的成员,原子类的成员还是按照自己类的访问限定符。

    1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它;

    既然不能访问,继承下来有什么意义?

    这是因为对于编译器来说,让部分成员不继承下去比让全部成员都继承下去更难,所以干脆就全部成员都继承下去,但是不想让你访问到的,就设置为私有就行了;

    1. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的;

    也就是说,protected和private对于父类来说,是没有区别的,该成员都是无法被访问;而对于子类来说,protected成员可以访问,private成员不可以访问。

    那public对于子类来说又意味着什么?

    protected和public对于子类来说没有区别,但是它们的区别是非派生类能否访问到该成员:保护的不可以,公有的可以。

    1. 实际对于这些继承方式和访问方式我们进行一下总结会发现,基类的私有成员在子类都是不可见。 基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected> private;

    可以比喻成木桶原理:一个木桶能装多少水取决于最短的那根木板。在这里就是说即使在父类中该成员是公有的,但是由于你的继承方式是private或者protected,那么继承下来的也就只能是保护或者私有,相当于最短的那根木板。而不存在私有成员被公有形式继承下来后就变成了公有成员的情况。

    1. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式;

    2. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强

    3. 基类和派生类对象赋值转换

    3.1 切片

    派生类对象可以赋值给基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。

    定义子类和父类:

    class Person
    {
    protected :
    	string _name; // 姓名
    	string _sex;  // 性别
    	int _age; // 年龄
    };
    class Student : public Person
    {
    public :
    	int _No ; // 学号
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    子类对象可以赋值给父类对象,这个过程会发生切片:

    Student sobj ;
    // 1.子类对象可以赋值给父类对象/指针/引用
    Person pobj = sobj;
    Person* pp = &sobj;
    Person& rp = sobj;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 对于对象赋值Person pobj = sobj:
      在这里插入图片描述

    这个很好理解,子类继承了父类,那当然是会比父类多成员,因为子类既有父类继承下来的那部分,又有自己原来的那部分。

    • 对于指针赋值Person* pp = &sobj:
      在这里插入图片描述
    • 对于引用赋值Person& rp = sobj:

    在这里插入图片描述

    3.2 注意的点

    1. 切片的过程是语法天然支持的行为,不存在类型转换

    我们知道不同类型变量之间的赋值存在隐式类型转换,但是切片的过程并不存在类型转换;就比如传引用,如果存在类型转换,那必定会有临时变量的出现,那么左值必须得是const常量才可以被赋值,但是Person& rp 很明显不是const常量(主页的引用章节有提到这个知识点)。

    1. 切片只针对公有继承

    例如父类有一个公有成员tmp,子类通过私有继承,得到了父类的tmp,对于子类来说是私有的,但是赋值给了父类以后(发生切片),在父类看来tmp还是公有的,那么就会把它当成公有来使用,这当然是不合理的。

    1. 基类对象不能赋值给派生类对象。

    父类对象的成员比子类对象少,切片就会越界,所以这种行为是不允许的,即使是强制类型转换为子类,也不允许。

    但是这对于指针和引用却是可以的,只是会有越界的风险: 强转后赋值给指针/引用,它们都会认为自己占用了某段空间,但是实际上已经超出了自己的范围,因为它们的实际内存范围是父类的空间范围,即使强转后,父类的内存范围里也没有子类的某些成员,但是它们会以为自己有,那么访问就会导致非法访问。

    在这里插入图片描述

  • 相关阅读:
    解决pycharm每次新建项目都要重新pip安装一些第三方库等问题
    代码随想录算法训练营第五十天|123.买卖股票的最佳时机III、188. 买卖股票的最佳时机 IV
    No boot device found快速解决方案
    【目标检测】39、一文看懂计算机视觉中的数据增强
    单链表在线OJ题(详解+图解)
    oracle统计信息
    JS的垃圾回收机制
    22年11月-外包-第四轮面试题
    .sql数据库导入错误:/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */
    算法进阶——最小的K个数
  • 原文地址:https://blog.csdn.net/weixin_52669146/article/details/127704855