C++进阶:继承

文章目录

  • 继承的概念
  • 继承的定义方式
  • 继承关系和访问限定符
  • 基类和派生类对象的赋值转换
  • 继承中的作用域
  • 派生类中的默认成员函数
    • 构造函数
    • 拷贝构造函数
    • 赋值拷贝函数
    • 析构函数
  • 总结

在这里插入图片描述

继承的概念

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

继承的使用场景,当我们有很多个类时,很多个类都有一些共同的特性,有共同的成员变量或者成员函数时,我们就可以把这些特性封装成一个公共的基类,再通过这些类继承这个公共的基类,如果个别类有独特的特性的话,可以单独写在成员变量或者成员函数当中。

在这里插入图片描述

继承的定义方式

class A
{
private:public:int _a;
};
class B :public A
{
public:private:int _b;
};

上面的代码是B类继承A类,A类作为基类,B类作为派生类
在这里插入图片描述

继承关系和访问限定符

在这里插入图片描述
继承方式有三种,public继承,private继承,protected继承,继承方式可以显示写,也可以不写,如果不写的话,class的默认继承方式是private,struct默认继承方式是public。
访问限定符有三种:public,private,protected
这里有一个规则 : 基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 = Min(成员在基类的访问限定符,继承方式)public > protected>private。大概规则就是在继承方式和访问限定符中取最小的一个,如果访问限定符是public,但是继承方式是private,那么最后就取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继承下来的成员都只能在派生类的类里
    面使用,实际中扩展维护性不强

基类和派生类对象的赋值转换

class A
{
private:public:int _a;
};
class B :public A
{
public:B(){}
private:int _b;
};

对于上面这个类来说,我们做以下事情。

int main()
{B b;A a;a = b;A& ref = b;A* Ptr = &b;return 0;
}

这是不是类型转换呢?很显然a和b不是一个类型,如果我们只看赋值操作的话,可能还会认为可能是类型转换,但是如果我们看看下面的引用ref等于b的话就会打消这个念头了,我们知道类型转换会产生临时变量,临时变量具有常性,所以这里引用不能引用一个具有常性的变量,所以这里应该加const,但是实践过程当中这里并没有报错,所以这里肯定不是类型转换,这里是一个特殊规则:赋值兼容转换
什么事赋值兼容转换呢?
派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
或者切割。寓意把派生类中父类那部分切来赋值过去。
我们可以这样理解:
在这里插入图片描述
赋值兼容转换可以理解为,我们将一个派生类拷贝给一个基类实际上是把派生类中的基类部分拿出来单独给基类,这里就像切片一样,把派生类切开,把基类的部分给基类,所以这里才取名为切片或者切割。

int main()
{B b;A a;a = b;A& ref = b;A* Ptr = &b;return 0;
}

这里如果我们 改变ref对应的成员变量相应的b中的对应的继承下来的成员变量也会变,指针也相同,这里我们讨论的是public情况下,如果是private就不能进行赋值兼容转换。

继承中的作用域

首先我们讨论一下,基类和派生类中是否可以存在相同的函数或者变量,答案是肯定的,首先作用域不同,可以存在相同的函数或者变量。

class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){A::fun();cout << "func(int i)->" <<i<<endl;}
};

对于上面这两个类来说,下面这个函数调用的是哪个fun呢?

void Test()B b;b.fun(10);
};

这里很显然调用的是B中的fun(),因为有参数,但是下面这个呢?

void Test()B b;b.fun();
};

上面这个调用的哪个fun呢?这里就不卖关子了,这里会报错。
在这里插入图片描述
这里直接会报编译错误,如果我们想调用A类中的函数怎么办呢?
我们可以像下面一样调用。

void Test()
{B b;b.A::fun();
};

像上面这样调用就可以直接调用A类中的相同函数。

这里我们提一下,这两个同名函数的关系,这两个同名函数构成隐藏。

派生类中的默认成员函数

构造函数

由于我们是先继承的基类的成员,所以我们实例化的时候,也是先定义的基类的成员,所以在初始化的时候我们也是先初始化基类的成员变量
在这里插入图片描述
可以看到这里先初始化的是基类的成员,即便我将基类的构造函数写在初始化列表的后面,根据这个也可以断定基类的成员变量是在派生类原本的成员变量之前定义的,因为初始化列表的初始化顺序是和声明的顺序一致的,这说明在派生类中继承的基类的成员是先声明的,这也说明应该先初始化基类成员再初始化派生类成员。

拷贝构造函数

class A
{
public:A(int a = 0):_a(a){}A(const A& a):_a(a._a){}
private:int _a;
};
class B : public A
{
public:B(int a = 0, int b = 0):_b(b),A(a){}B(const B& b):_b(b._b),A(b){}
private:int _b;
};

对于拷贝构造来说这里就要用到刚刚我们刚刚讲到的切片,也就是赋值兼容转换,如果是派生类的成员变量的话,就直接走初始化列表直接进行初始化,但是对于基类的成员这里我们直接调用基类的默认的拷贝构造函数,这里我们直接传的变量是类型为B的,这里会做一个切片,直接将属于传递过去用的是A类接收,所以这里会做一个切片,传递过去只接受了A类的部分。

赋值拷贝函数

	//A类的赋值拷贝函数A& operator=(const A& a){_a = a._a;}//B类的赋值拷贝函数B& operator=(const B& b){_b = b._b;A::operator=(b);}

需要注意的是,我们在复用基类的赋值拷贝函数的时候,需要说明一下作用域,因为两个赋值拷贝函数构成隐藏,所以调用的时候会优先调用派生类的赋值拷贝函数,所以这里如果不指定作用域的话,会出现无穷递归,很明显下面就出现了无穷递归,所以这里要指定作用域。
在这里插入图片描述

析构函数

对于析构函数来说,首先我们要知道的是,在我们调用构造函数的时候,先初始化的是基类的成员,所以应该是基类成员先初始化,然后再初始化派生类自身的成员,所以这里应该是基类的成员先入栈,派生类再入栈,所以这里我们应该先保证先析构派生类的成员变量,再析构基类的成员变量,我们需要保证这一点,由于C++的机制,C++在析构完派生类的成员变量的时候,会直接自动调用基类的析构函数,所以我们并不用显示调用析构函数,但是如果我们也可以显示调用。
在这里插入图片描述

这里是因为多态的某些原因,后面细讲。
这里应该指定作用域,所以应该像下面一样调用。
在这里插入图片描述
但是我们可以看见,一个对象调用了两次析构,而且还是先析构的是基类成员,所以这里我们就不需要显示写出来,直接等编译器自动调用即可。

总结

继承是面向对象编程中的一个核心概念,通过它我们能够实现代码的重用和扩展。在本篇博客中,我们从继承的基本概念开始,逐步深入探讨了继承的定义方式、继承关系中的访问限定符以及基类和派生类对象之间的赋值转换。我们还讨论了继承中的作用域问题以及派生类中的默认成员函数,包括构造函数、拷贝构造函数、赋值拷贝函数和析构函数。

特别地,我们重点分析了析构函数在多态性中的作用。通过将基类的析构函数声明为虚函数,确保在基类指针指向派生类对象时,能够正确调用派生类的析构函数,从而避免资源泄露和内存管理问题。

通过这些内容的学习,我们不仅理解了继承的基本原理和实现方法,还掌握了如何在实际编程中应用这些知识,提高代码的可维护性和扩展性。希望这些内容能为大家提供有价值的参考,帮助大家更好地理解和应用继承这一重要的编程概念。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【全开源】Java无人共享棋牌室茶室台球室系统JAVA版本支持微信小程序+微信公众号

无人共享棋牌室系统——棋牌娱乐新体验 &#x1f3b2;引言 随着科技的不断发展&#xff0c;传统棋牌室正逐渐迈向智能化、无人化。今天&#xff0c;我要为大家介绍的就是这款引领潮流的“无人共享棋牌室系统”。它不仅为棋牌爱好者提供了全新的娱乐体验&#xff0c;更在便捷性…

【Python】数据处理:SQLite操作

使用 Python 与 SQLite 进行交互非常方便。SQLite 是一个轻量级的关系数据库&#xff0c;Python 标准库中包含一个名为 sqlite3 的模块&#xff0c;可以直接使用。 import sqlite3数据库连接和管理 连接到 SQLite 数据库。如果数据库文件不存在&#xff0c;则创建一个新数据库…

Conda安装

conda可以做到不同项目就用不同虚拟环境,这样就能做到每个项目的依赖包都是相互独立 一、windows Download Success | Anaconda 环境变量 二、nano 本次安装Archiconda的外部python版本为python3.7.1

Vue基础知识:异步DOM更新是什么?$nextTick是什么?到底应该如何使用。什么是同步?什么是异步?

要先了解异步dom更新是什么就必须先了解&#xff0c;什么是同步&#xff1f;什么是异步&#xff1f; 1.什么是同步&#xff1f;什么是异步&#xff1f; 同步&#xff08;Synchronous&#xff09;&#xff1a; 同步操作是按照代码的顺序执行的&#xff0c;每个操作都必须等待上…

【数学建模】MATLAB入门教程:插值与拟合(下)

前言 插值与拟合在数据处理和科学计算中扮演着非常重要的角色&#xff0c;它们用于估算未知数据点的值&#xff0c;帮助我们理解和预测数据趋势 一、一维插值 1、一维插值定义 已知n1个节点(,)(j0,1,...,n,其中互不相同&#xff0c;不妨设a<<...<b),求任一插值点(…

swift5 在当前控制器先dismiss后pop

如下图需要在present当前控制器时用全局变量firmwareUpgradePresentingVC先引用上一个控制器&#xff08;下面的代码亲测有效&#xff09; func dismissAndPop() {self.dismiss(animated: false) {firmwareUpgradePresentingVC.navigationController!.popViewController(animat…

Java基础面试重点-3

41. 简述线程生命周期(状态) 其它参考《多线程重点》中的说法。三种阻塞&#xff1a; 等待阻塞&#xff1a; 运行的线程执行o.wait()方法&#xff08;该线程已经持有锁&#xff09;&#xff0c;JVM会把该线程放入等待队列中。同步阻塞&#xff1a; 运行的线程在获取对象的同步…

Stable Diffusion 如何写出更优雅的 Prompt

在看了前面的课程后&#xff0c; 相信很多人都会有一个困惑&#xff0c;这个 prompt 咋写… 为什么我写的时候只能憋出来了一个 a girl, a boy, beautify … 再也想不到其他的了&#xff0c; 总感觉是吃了没文化的亏&#xff1f; 这一节课我们就来讲一讲 如何写好 prompt …

跟着AI学AI_08 NumPy 介绍

NumPy&#xff08;Numerical Python&#xff09;是一个用于科学计算的基础库&#xff0c;它为 Python 提供了支持大规模多维数组和矩阵 NumPy 介绍 NumPy&#xff08;Numerical Python&#xff09;是一个用于科学计算的基础库&#xff0c;它为 Python 提供了支持大规模多维数…

如何快速搭建自己的进销存系统?

什么是进销存系统&#xff1f; 进销存&#xff0c;是指企业管理过程中采购&#xff08;进&#xff09;—入库&#xff08;存&#xff09;—销售&#xff08;销&#xff09;的动态管理过程。进&#xff1a;指询价、采购到入库与付款的过程。进销存管理系统是对企业生产经营中物…

【Python】已完美解决:(Python键盘中断报错问题) KeyboardInterrupt

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例&#xff08;结合实战场景&#xff09;五、注意事项 已解决&#xff1a;Python中处理KeyboardInterrupt&#xff08;键盘中断&#xff09;报错问题 一、问题背景 在Python编程中&#xff0c;当我们运…

css系列:音频播放效果-波纹律动

介绍 语音播放的律动效果&#xff0c;通俗来说就是一个带动画的特殊样式的进度条&#xff0c;播放的部分带有上下律动的动画&#xff0c;未播放的部分是普通的灰色竖状条。 实现中夹带了less变量、继承和循环遍历&#xff0c;可以顺带学习一下。 结果展示 大致效果如图所示…

继承深度剖析

前言 从继承开始就开始C进阶了&#xff0c; 这一块需要好好学习&#xff0c;这块知识很重要&#xff0c; 坑有点多&#xff0c;所以是面试笔试的常客。 基本概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c; 它允许程序员在保持原有…

k8s学习--kubernetes服务自动伸缩之水平伸缩(pod副本伸缩)HPA详细解释与案例应用

文章目录 前言HPA简介简单理解详细解释HPA 的工作原理监控系统负载模式HPA 的优势使用 HPA 的注意事项应用类型 应用环境1.metircs-server部署2.HPA演示示例&#xff08;1&#xff09;部署一个服务&#xff08;2&#xff09;创建HPA对象&#xff08;3&#xff09;执行压测 前言…

记一次 .NET某工厂报警监控设置 崩溃分析

一&#xff1a;背景 1. 讲故事 前些天有位朋友在微信上丢了一个崩溃的dump给我&#xff0c;让我帮忙看下为什么出现了崩溃&#xff0c;在 Windows 的事件查看器上显示的是经典的 访问违例 &#xff0c;即 c0000005 错误码&#xff0c;不管怎么说有dump就可以上windbg开干了。…

4090显卡 安装cuda 11.3 版本

文章目录 cuda 安装安装过程中会要求选择安装的内容更改cuda地址到你安装的地方 cuda 安装 cuda官网寻找cuda11.3 版本 https://developer.nvidia.com/cuda-11.3.0-download-archive?target_osLinux&target_archx86_64&DistributionUbuntu&target_version20.04&…

setOptMode -holdTargetSlack与-holdSlackFixingThreshod

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 -holdTargetSlack与-holdSlackFixingThreshod这两个option都是针对hold slack的&#xff0c;前者限制slack的目标&#xff0c;默认是0&#xff0c;也就是说工具尽可能会收敛时序…

解决electron设置透明背景后,引入element-plus样式问题

首先给当前窗口设置自定义窗口以及背景色。 const mainWindow new BrowserWindow({width: 900,height: 670,show: false,autoHideMenuBar: true,...(process.platform linux ? { icon } : {}),webPreferences: {preload: join(__dirname, ../preload/index.js),sandbox: fal…

操作系统入门系列-MIT6.828(操作系统工程)学习笔记(七)---- 系统调用函数与GDB(Lab: system calls)

系列文章目录 操作系统入门系列-MIT6.828&#xff08;操作系统工程&#xff09;学习笔记&#xff08;一&#xff09;---- 操作系统介绍与接口示例 操作系统入门系列-MIT6.828&#xff08;操作系统工程&#xff09;学习笔记&#xff08;二&#xff09;---- 课程实验环境搭建&am…

深度学习500问——Chapter11:迁移学习(1)

文章目录 11.1 迁移学习基础知识 11.1.1 什么是迁移学习 11.1.2 为什么需要迁移学习 11.1.3 迁移学习的基本问题有哪些 11.1.4 迁移学习有哪些常用概念 11.1.5 迁移学习与传统机器学习有什么区别 11.1.6 迁移学习的核心及度量准则 11.1.7 迁移学习与其他概念的区别 11.1.8 什么…