当前位置: 首页 > news >正文

【C++】万字一文全解【继承】及其特性__[剖析底层化繁为简](20)

前言

大家好吖,欢迎来到 YY 滴C++系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁
主要内容含:
在这里插入图片描述

欢迎订阅 YY滴C++专栏!更多干货持续更新!以下是传送门!

目录

  • 一.继承&复用&组合的区别
    • 1)函数复用与继承区别
    • 2)复用的分类
      • [1]白箱复用——继承
      • [2]黑箱复用——组合(优先)
  • 二.继承的基本格式与继承以后的访问方式变化(基类成员)
    • 1)基本格式
    • 2)三种继承方式
    • 3)在派生类中不可见
    • 4)基类成员经过不同继承以后分别到派生类的什么作用域中【访问方式变化】
    • 5)struct和class的默认继承方式
    • 6)实际运用中一般使用都是public继承的原因
  • 三.基类和派生类对象赋值转换【切片概念】
  • 四.继承中的【隐藏】
  • 五.派生类的默认成员函数生成机制
  • 六.“友元关系”不能被继承
  • 七.基类定义了static静态成员,整个继承体系里面只有一个这样的成员
  • 八.复杂的菱形继承及菱形虚拟继承
    • 1)菱形继承
    • 2)解决菱形继承问题方法:虚拟继承
    • 3)虚拟继承解决菱形继承原理————虚基表&虚基表指针&利用偏移量
      • 【1】虚拟继承前后的内存模型变化

一.继承&复用&组合的区别

1)函数复用与继承区别

函数复用与继承区别:

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

2)复用的分类

组合与继承:

  • public继承是一种is-a的关系。也就是说每个派生类对象 都是一个基类对象:相当于[人]与[学生]&[老师]的关系
  • 组合是一种has-a的关系。假设B组合了A,每个B对象中 都有一个A对象:相当于[手]与[人]的关系
  • 优先使用对象组合,而不是类继承
  • 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有
    些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
    继承,可以用组合,就用组合

[1]白箱复用——继承

  • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称 为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的 内部细节对子类可见。 继承一定程度破坏了基类的封装 ,基类的改变,对派生类有很 大的影响。派生类和基类间的依赖关系很强, 耦合度高

[2]黑箱复用——组合(优先)

  • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。 对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为 黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装.

二.继承的基本格式与继承以后的访问方式变化(基类成员)

1)基本格式

在这里插入图片描述

2)三种继承方式

  • 继承方式分为三种:public继承,protect继承,private继承
  • 保护访问限定符专门为继承而产生:基类private成员在派生类中是不能被访问 ,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为 protected 。可以看出保护成员限定符是因继承才出现的。

3)在派生类中不可见

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

4)基类成员经过不同继承以后分别到派生类的什么作用域中【访问方式变化】

记忆与理解:

  1. 权限大小:public > protect > private
  2. 基类中的public成员,经过什么继承,就到派生域的什么作用域中
  3. 【以权限小的为主】基类中的protect成员,权限小于public:经过public继承还是到protect作用域中,其权限又大于private,继承,经过private继承后到private作用域中
  4. 基类中的private成员,经过任何继承都在派生类中不可见
    在这里插入图片描述

5)struct和class的默认继承方式

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

6)实际运用中一般使用都是public继承的原因

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

三.基类和派生类对象赋值转换【切片概念】

  • 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
    或者切割。寓意 把派生类中父类那部分 切来赋值过去
  • 基类对象不能赋值给派生类对象
  • 基类的指针或者引用可以通过 强制类型转换 赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的
    在这里插入图片描述
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 = &sobjStudent* 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的情况下,子类析构函数和父类析构函数构成隐藏关系】

六.“友元关系”不能被继承

  • 友元关系不能继承,也就是说基类友元不能访问子类 私有 保护 成员
//fail
class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{
protected:int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}
void main()
{Person p;Student s;Display(p, s);
}

七.基类定义了static静态成员,整个继承体系里面只有一个这样的成员

  • 基类定义了static静态成员,则整个继承体系里面 只有一个 这样的成员——即无论派生出多少个子类,都只有一个static成员实例
class Person
{
public :Person () {++ _count ;}                 //派生类会调用基类的构造
protected :string _name ; // 姓名
public :static int _count; // 统计人的个数。
};
int Person :: _count = 0;
class Student : public Person
{
protected :int _stuNum ; // 学号
};
class Graduate : public Student
{
protected :string _seminarCourse ; // 研究科目
};
void TestPerson()
{Student s1 ;Student s2 ;Student s3 ;Graduate s4 ;cout <<" 人数 :"<< Person ::_count << endl;  //输出结果为4Student ::_count = 0;                   cout <<" 人数 :"<< Person ::_count << endl;  //输出结果为0
}

八.复杂的菱形继承及菱形虚拟继承

1)菱形继承

  • 在面向对象中,常常存在这样的事情,一个派生类它有两个或两个以上的基类,这种行为称作多重继承,示意图如下:
    在这里插入图片描述
  • 在多重继承的基础上,Class Student 和Class Teacher 存在同名数据成员,则对Class Person而言这个同名的数据成员容易产生 二义性问题
  • 菱形继承还会产生 数据冗余 现象;在Assistant的对象中Person成员会有两份;
    在这里插入图片描述
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";
}

2)解决菱形继承问题方法:虚拟继承

  • 虚拟继承 可以解决菱形继承的二义性和数据冗余的问题。如下图代码中 在继承关系前加上“virtual” ,在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";
}

3)虚拟继承解决菱形继承原理————虚基表&虚基表指针&利用偏移量

【1】虚拟继承前后的内存模型变化

  • 虚拟继承前:
    在这里插入图片描述
  • 虚拟继承后:
    在这里插入图片描述
  • 这里可以分析出D对象中将A放到的了 对象组成的最下面 ,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫 虚基表指针 ,这两个表叫 虚基表 。虚基表中存的 偏移量 通过偏移量可以找到下面的A

相关文章:

【C++】万字一文全解【继承】及其特性__[剖析底层化繁为简](20)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.继承&复用&组合的区别1&…...

微信小程序之自定义组件开发

1、前言 从小程序基础库版本 1.6.3 开始&#xff0c;小程序支持简洁的组件化编程。所有自定义组件相关特性都需要基础库版本 1.6.3 或更高。开发者可以将页面内的功能模块抽象成自定义组件&#xff0c;以便在不同的页面中重复使用&#xff1b;也可以将复杂的页面拆分成多个低耦…...

MCU系统的调试技巧

MCU系统的调试技巧对于确保系统稳定性和性能至关重要。无论是在嵌入式系统开发的初期阶段还是在产品维护和优化的过程中&#xff0c;有效的调试技巧可以帮助开发人员快速发现和解决问题&#xff0c;本文将讨论一些MCU系统调试的技巧。 首先&#xff0c;使用调试工具是非常重要…...

【机器学习基础】机器学习概述

目录 前言 一、机器学习概念 二、机器学习分类 三、机器学习术语 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da;。 &#x…...

Python Selenium 执行 JavaScript

简介 Selenium是一个用于自动化浏览器操作的工具&#xff0c;可以模拟人工操作&#xff0c;执行各种浏览器操作&#xff0c;包括点击、输入文字、提交表单等。而JavaScript是一种常用的脚本语言&#xff0c;用于在网页上添加交互性和动态性。在Python中使用Selenium执行JavaSc…...

HTML的表单标签和无语义标签的讲解

HTML的表单标签 表单是让用户输入信息的重要途径, 分成两个部分: 表单域: 包含表单元素的区域. 重点是 form 标签. 表单控件: 输入框, 提交按钮等. 重点是 input 标签 form 标签 使用form进行前后端交互.把页面上,用户进行的操作/输入提交到服务器上 input 标签 有很多形态,能…...

8.spark自适应查询-AQE之自适应调整Shuffle分区数量

目录 概述主要功能自适应调整Shuffle分区数量原理默认环境配置修改配置 结束 概述 自适应查询执行&#xff08;AQE&#xff09;是 Spark SQL中的一种优化技术&#xff0c;它利用运行时统计信息来选择最高效的查询执行计划&#xff0c;自Apache Spark 3.2.0以来默认启用该计划。…...

【Java 进阶篇】Java Filter 快速入门

欢迎来到这篇有关 Java Filter 的快速入门指南&#xff01;如果你是一名 Java 开发者或者正在学习 Java Web 开发&#xff0c;Filter 是一个强大的工具&#xff0c;可以帮助你管理和控制 Web 应用程序中的请求和响应。本文将向你解释 Filter 的基本概念&#xff0c;如何创建和配…...

Pytorch R-CNN目标检测-汽车car

概述 目标检测(Object Detection)就是一种基于目标几何和统计特征的图像分割,它将目标的分割和识别合二为一,通俗点说就是给定一张图片要精确的定位到物体所在位置,并完成对物体类别的识别。其准确性和实时性是整个系统的一项重要能力。 R-CNN的全称是Region-CNN(区域卷积神经…...

【PG】PostgreSQL13主从流复制部署(详细可用)

目录 版本 部署主从注意点 1 主库上创建复制用户 2 主库上修改pg_hba.conf文件 3 修改文件后重新加载配置使其生效 4 主库上修改配置文件 5 重启主库pg使参数生效 6 部署从库 7 备份主库数据至从库 停止从库 备份从库的数据库目录 新建数据库数据目录data 创建和…...

学习pytorch15 优化器

优化器 官网如何构造一个优化器优化器的step方法coderunning log出现下面问题如何做反向优化&#xff1f; 官网 https://pytorch.org/docs/stable/optim.html 提问&#xff1a;优化器是什么 要优化什么 优化能干什么 优化是为了解决什么问题 优化模型参数 如何构造一个优化器…...

[算法日志]图论刷题 沉岛思想的运用

[算法日志]图论刷题: 沉岛思想的运用 leetcode 695 岛屿最大面积 给你一个大小为 m x n 的二进制矩阵 grid . 岛屿 是由一些相邻的 1 (代表土地) 构成的组合, 这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻. 你可以假设 grid 的四个边缘都被 0&#xff08…...

Web服务器的搭建

网站需求&#xff1a; 1.基于域名www.openlab.com可以访问网站内容为 welcome to openlab!!! 2.给该公司创建三个网站目录分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于www.openlab.com/student 网站访问学生信息&#xff0c;www.openlab.com/data网站访问教…...

如何使用 GTX750 或 1050 显卡安装 CUDA11+

前言 由于兼容性问题&#xff0c;使得我们若想用较新版本的 PyTorch&#xff0c;通过 GPU 方式训练模型&#xff0c;也得更换较新版本得 CUDA 工具包。然而 CUDA 的版本又与电脑显卡的驱动程序版本关联&#xff0c;如果是低版本的显卡驱动程序安装 CUDA11 及以上肯定会失败。 比…...

跟着森老师学React Hooks(1)——使用Vite构建React项目

Vite是一款构建工具&#xff0c;对ts有很好的支持&#xff0c;最近也是在前端越来越流行。 以往的React项目的初始化方式大多是通过脚手架create-react-app(本质是webpack)&#xff0c;其实比起Vite来构建&#xff0c;启动会慢一些。 所以这次跟着B站的一个教程&#xff0c;使用…...

强力解决使用node版本管理工具 NVM 出现的问题(找不到 node,或者找不到 npm)

强力解决使用node版本管理工具 NVM 出现的问题&#xff08;找不到 node&#xff0c;或者找不到 npm&#xff09; node与npm版本对应关系 nvm是好用的Nodejs版本管理工具&#xff0c; 通过它可以方便地在本地调换Node版本。 2020-05-28 Node当前长期稳定版12.17.0&#xff0c;…...

Docker指定容器使用内存

Docker指定容器使用内存 作者&#xff1a;铁乐与猫 如果是还没有生成的容器&#xff0c;你可以从指定镜像生成容器时特意加上 run -m 256m 或 --memory-swap512m来限制。 -m操作指定的是物理内存&#xff0c;还有虚拟交换分区默认也会生成同样的大小&#xff0c;而–memory-…...

做什么数据表格啊,要做就做数据可视化

是一堆数字更易懂&#xff0c;还是图表更易懂&#xff1f;很明显是图表&#xff0c;特别是数据可视化图表。数据可视化是一种将大量数据转化为视觉形式的过程&#xff0c;通过图形、图表、图像等方式呈现数据&#xff0c;以便更直观地理解和分析。 数据可视化更加生动、形象地…...

CSS特效003:太阳、地球、月球的旋转

GPT能够很好的应用到我们的代码开发中&#xff0c;能够提高开发速度。你可以利用其代码&#xff0c;做出一定的更改&#xff0c;然后实现效能。 css实战中&#xff0c;这种球体间的旋转&#xff0c;主要通过rotate()旋转函数来实现。实际上&#xff0c;蓝色的地球和黑色的月球…...

云计算的大模型之争,亚马逊云科技落后了?

文丨智能相对论 作者丨沈浪 “OpenAI使用了Azure的智能云服务”——在过去的半年&#xff0c;这几乎成为了微软智能云最好的广告词。 正所谓“水涨船高”&#xff0c;凭借OpenAI旗下的ChatGPT在全球范围内爆发&#xff0c;微软趁势拉了一波自家的云计算业务。2023年二季度&a…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...