继承的相关介绍---C++

news2025/1/11 8:18:43

一、概念及定义

概念:

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。

定义:

继承的格式如下:

class Student: public Person
{}

这里的Person是父类,也称为基类;Student是子类,也称派生类,是继承Pereson的

继承方式包括:public继承、protected继承、private继承

访问限定符包括:public访问、protected访问、private访问

如下是继承基类成员访问方式的变化:

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected
成员
派生类的private
成员
基类的protected
成员
派生类的protected
成员
派生类的protected
成员
派生类的private
成员
基类的private成
在派生类中不可见在派生类中不可见在派生类中不可

总结:

1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员,但它是被继承到了派生类对象中,只是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected
> private。
4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过
最好显示的写出继承方式。
5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强
 

二、类对象赋值

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

2.基类对象不能赋值给派生类对象。
3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。

 通过下面一段代码进行解释:

class Person
{
    protected :
    string _name; // 姓名
    string _sex; // 性别
    int _age; // 年龄
};
class Student : public Person
{
    public :
    int _No ; // 学号
};
void Test ()
{
    Student sobj ;
// 1.子类对象可以赋值给父类对象/指针/引用
    Person pobj = sobj ;
    Person* pp = &sobj;
    Person& rp = sobj;
//2.基类对象不能赋值给派生类对象
    sobj = pobj;
// 3.基类的指针可以通过强制类型转换赋值给派生类的指针
    pp = &sobj
    Student* ps1 = (Student*)pp; // 这种情况转换时可以的。
    ps1->_No = 10;
    pp = &pobj;
    Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
    ps2->_No = 10;
}

三、继承中的作用域

1. 在继承体系中基类和派生类都有独立的作用域。
2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
4. 建议在实际中在继承体系里面最好不要定义同名的成员。

注意:不要将重载和隐藏混淆了,重载的条件之一是要在同一作用域,而隐藏只需要满足函数名相同就能构成

四、派生类的默认成员函数

派生类一共有6个默认的成员函数,这个默认是指不用我们写,会自己生成:

这六个成员函数是如何生成的呢?

1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。

五、继承与友元、静态成员

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子
类,都只有一个static成员实例 。

六、继承方式

继承分为:单继承、多继承和菱形继承。

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

 菱形继承:多继承的一种特殊情况。

 

前两种继承方式都很简单,主要针对有关多继承的相关问题:请看如下代码块

class Person
{
public :
    string _name ; // 姓名
};
class Student : public Person
{
protected :
    int _num ; //学号
};
class Teacher : public Person
{
protected :
    int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
    string _majorCourse ; // 主修课程
};
void Test ()
{
// 这样会有二义性无法明确知道访问的是哪一个
    Assistant a ;
    a._name = "peter";
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
    a.Student::_name = "xxx";
    a.Teacher::_name = "yyy";
}

可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。

那么要如何解决·这个问题呢?那就可以考虑到虚继承(virtural)。

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。

class Person
{
public :
    string _name ; // 姓名
};
class Student : virtual public Person
{
protected :
    int _num ; //学号
};
class Teacher : virtual public Person
{
protected :
    int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
    string _majorCourse ; // 主修课程
};
void Test ()
{
    Assistant a ;
    a._name = "peter";
}

原理:

七、总结

1.public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
2.组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
3.优先使用对象组合,而不是类继承 。
4.继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
5.对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
6.实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/482584.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

复现CVE-2023-21839

攻击机安装jdk1.8 下载jdk1.8 https://www.azul.com/downloads/?versionjava-8-lts&osubuntu&architecturex86-64-bit&packagejdk#zulu 或 wget https://cdn.azul.com/zulu/bin/zulu8.60.0.21-ca-jdk8.0.372-linux_x64.tar.gz tar -zxvf zulu8.60.0.21-ca-jdk8.…

GB/T 28181-2011、2016、2022变更对比

一、GB/T 28181-2016与GB/T 28181-2011变更对比 GB/T 28181-2016与GB/T 28181-2011相比, 除编辑性修改外主要技术变化如下: ----(1) 修改了标准名称; ----(2) 增加了媒体流TCP传输要求(见4.3.1, 5.2,附录F&#xff…

Ubuntu磁盘和目录和文件的相关操作

目录 1、目录的切换 2、查看目录及文件 3、目录的常见操作 4、文件的常见操作 5、查看文件及目录大小 6、命令查看硬盘信息 1、目录的切换 打开终端窗口(”ctrlaltt“) 一般使用(”pwd“)显示当前所在的目录 比如&#x…

【用python的QT做信号处理的界面】

文章目录 入口文件界面参数调整数据从dat解析出来的文件从界面点击打开文件夹的功能实现主要功能代码网络参数存图替换功能,比如把倒频谱替换成倒频谱2 入口文件 入口文件,主要用来实例化窗口(不重要),只要知道从这里…

电脑中病毒了怎么修复,计算机Windows系统预防faust勒索病毒方法

随着计算机系统的不断发展,我们所面对的网络安全威胁也变得越来越严重。其中,较为常见且危险的威胁就是勒索病毒。随着勒索病毒加密算法的不断升级,最近faust勒索病毒开始流行。Faust勒索病毒主要的攻击目标是Windows操作系统,一旦…

SpringBoot手册

目录 依赖管理关于各种的 start 依赖关于自动配置关于约定大于配置中的配置SpringBoot 整合 SpringMVC定制化 SpringMVC静态资源处理对上传文件的处理对异常的处理Web原生组件注入(Servlet、Filter、Listener)Interceptor 自定义拦截器DispatcherServlet…

【iOS】GCD学习

GCD的概念 GCD(Grand Central Dispatch),是有Apple公司开发的一个多核编程的解决方案,用以优化应用程序支持多核处理器,是基于线程模式之上执行并发任务。 GCD的优点 利用设备多核进行并行运算GCD自动充分使用设备的…

C语言-学习之路-03

C语言-学习之路-03 程序流程结构选择结构if语句if...else...语句三目运算符switch语句 循环结构while语句do...while语句for语句嵌套循环 跳转语句break、continue、gotobreak语句continue语句goto语句 程序流程结构 C语言支持最基本的三种程序流程结构:顺序结构、…

三、SpringMVC

三、SpringMVC 1、SpringMVC简介 1.1、什么是MVC MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分 M:Model,模型层,指工程中的JavaBean,作用是处理数据 JavaBean分为两类: 一类称为实体…

REST API 详解

REST API REST(Representational State Transfer,表述性状态转移)是一种用于构建分布式系统的架构风格。REST API(Application Programming Interface,应用程序接口)是一种基于REST风格的网络API&#xff…

Filter 过滤器

Filter过滤器介绍 这里我们讲解Filter的执行流程,从下图可以大致了解到,当客户端发送请求的时候,会经过过滤器,然后才能到我们的servlet,当我们的servlet处理完请求之后,我们的response还是先经过过滤器才…

Packet Tracer - 配置交换机端口安全

Packet Tracer - 配置交换机端口安全 地址分配表 设备 接口 IP 地址 子网掩码 S1 VLAN 1 10.10.10.2 255.255.255.0 PC1 NIC 10.10.10.10 255.255.255.0 PC2 NIC 10.10.10.11 255.255.255.0 非法笔记本电脑 NIC 10.10.10.12 255.255.255.0 目标 第 1 部…

MySQL监控告警及可视化:Zabbix+Percona PMP实现(Part I)

MySQL监控告警及可视化:ZabbixPercona PMP实现(Part I) 准备工作Zabbix Server安装Zabbix Server配置conf文件配置系统服务配置Web服务配置 Zabbix Agent安装Zabbix Agent配置conf文件配置系统服务配置 准备工作 🐬软件下载&#…

ChatGPT提示词工程(五):Transforming转换

目录 一、说明二、安装环境三、转换(Transforming)1. 翻译 Translation2. 语气转换 Tone Transformation3. 格式转换 Format Conversion4. 拼写或语法检查 Spellcheck/Grammar check 一、说明 这是吴恩达 《ChatGPT Prompt Engineering for Developers》…

kubernetes安全框架RBAC

目录 一、Kubernetes 安全概述 二、鉴权、授权和准入控制 2.1 鉴权(Authentication) 2.2 授权(Authorization) 2.3 准入控制 三、基于角色的权限访问控制: RBAC 四、案例:为指定用户授权访问不同命名空间权限 一、Kubernetes 安全概述 K8S安全控…

YOLOv5 txt标签转图像标签(多个标签)

Python YOLOv5 txt标签转图像标签(多个标签 txt的数据如图所示1.读原始图像以及对应的txt文件2.获得原始图像的大小3.生成一张大小相同,黑色背景的图片4.读取txt文件,循环的增加标签5.获得不规则图形(标签)6.完整代码7…

Linux运维:makefile

一.makefile 1.makefile介绍 Makefile 是一种用于自动化构建的文件,它描述了一个软件项目的编译规则和依赖关系,并提供了一些工具来自动执行这些规则。 Makefile 的主要作用如下: 自动化编译:通过 Makefile,可以定…

使用 nvcc 的时候出现“helper_cuda.h”: No such file or directory错误该怎么办(其他xxx.h也是同样的解决方法)

今天在命令行中使用nvcc的时候,出现了“helper_cuda.h”: No such file or directory错误。这个错误信息表示:在编译的时候,找不到helper_cuda.h头文件(其他xxx.h如果也找不到,那么下面的解释和解决方法也通用&#xf…

Java流式操作——Collectors工具类

文章目录 方法实践说明一、前提条件Person类Data类 二、操作maxBy:获取流中最大元素;minBy:获取流中最小元素joining:合并,将流中的元素,以字符串的形式拼接起来summingInt:把流中的元素映射成int类型的元素&#xff0…

springboot第14集:MyBatis-CRUD讲解

注意点:增、删、改操作需要提交事务! 为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上! 编写Mapper接口类 import com.da.pojo.User; import java.util.List; public interface UserMapper…