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

【C++】详细讲解继承(下)

        本篇来继续说说继承。上篇可移步至【C++】详细讲解继承(上) 

1.继承与友元

友元关系不能继承 ,也就是说基类友元不能访问派⽣类私有和保护成员。
class Student;//前置声明class Same //基类
{
public:friend void Fun(const Same& p, const Student& s);//友元声明
protected:string _name; 
};class Student : public Same //派生类
{
protected:int _stuid;
};
void Fun(const Same& p, const Student& s)
{cout << p._name << endl;cout << s._stuid << endl;
}

像上面的代码,Fun函数只能访问Same基类的成员变量_name,_stuid是访问不到的。

 解决方法就是在派生类Student里面也加上友元声明,就可以了。

class Student : public Same //派生类
{friend void Fun(const Same& p, const Student& s);//友元声明
protected:int _stuid;
};

2.继承与静态成员

基类定义了static静态成员,则整个继承体系⾥⾯ 只有⼀个这样的成员 。⽆论派⽣出多少个派⽣类,都只有⼀个static成员实例。
class Same //基类
{
public:string _name;static int _count;//静态成员变量
};int Same::_count = 0;//静态成员变量初始化class Student : public Same //派生类
{
protected:int _stuNum;
};

 我们观察一下_name的地址和_count的地址。

int main()
{Same p;Student s;cout << &p._name << endl;cout << &s._name << endl;cout << &p._count << endl;cout << &s._count << endl;return 0;
}

这⾥的运⾏结果可以看到:⾮静态成员_name的地址是不⼀样的,说明派⽣类继承下来了,基类和派⽣类对象各有⼀份静态成员_count的地址是⼀样的,说明派⽣类和基类共⽤同⼀份静态成员。

公有情况下,基类和派生类指定类域都可以访问静态成员变量。

cout << Same::_count << endl;
cout << Student::_count << endl;

也可以通过对象访问。

 

 改变其中一个,另一个也改变,因为这就是同一个。

cout << Same::_count << endl;
cout << Student::_count << endl;
Same::_count++; //改变_count
cout << p._count << endl;
cout << s._count << endl;

3.多继承以及菱形继承

3.1 继承模型

  • 单继承:⼀个派⽣类只有⼀个直接基类时称这个继承关系为单继承
  • 多继承:⼀个派⽣类有两个或以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型是,先继承的基类在前⾯,后⾯继承的基类在后⾯,派⽣类成员在放到最后⾯。
  • 菱形继承:菱形继承是多继承的⼀种特殊情况,如下。

菱形继承有数据冗余⼆义性(存在歧义)的问题,在Assistant的对象中Person成员会有两份。所以在实践中并不提倡设计出菱形继承的模型。

 二义性问题可以通过指定访问哪个基类的成员来解决,但是数据冗余问题是不能得到解决的。

如果在特定场景下,一定需要设计菱形继承,怎么办?虚继承就出场了。

3.2 虚继承

新增了一个关键字virtual。放在会造成数据冗余和二义性的那些类上

这里就是在Student和Teacher 加上virtual,都要加,只加一个都没用。

class Person
{
public:string _name; // 姓名};
// 使⽤虚继承Person类
class Student : virtual public Person
{
protected:int _num; //学号
};
// 使⽤虚继承Person类
class Teacher : virtual public Person
{
protected:int _id; // 职⼯编号
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};

 加了virtual后person在Assistant里继承的数据就只有一份了,数据冗余和二义性就得到了解决。

3.3 相关小测试

假如现在有一个菱形继承关系如下, virtual应该加在哪里?

加在B类和C类上。因为E里面会有数据冗余和二义性,而这些冗余的数据是因为A有两份,导致继承A的是B和C,所以要加在B和C上,不是D和C。

 最后,除非万不得已,不要设计出菱形继承。菱形继承以及virtual的底层是特别复杂的。

3.4 多继承中指针偏移问题

先看题。

class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main()
{Derive d;Base1* p1 = &d;Base2* p2 = &d;Derive* p3 = &d;return 0;
}

说法正确的是?

A :p1 == p2 == p3    B :p1 < p2 < p3    C :p1 == p3 != p2    D :p1 != p2 != p3
答案 C :p1 == p3 != p2 
先继承的在前面 ,先声明的在前面,所以Base1和Base2的底层位置如下。
p3是Derive的指针,指向开头。
p1是Base1的指针,Base1是基类,p1指向的范围是派生类Derive切出来的Base1的那部分,最开始指向开头,和p3一个位置。
p2与p1同理,指向的范围是Derive切出来的Base2的那部分,最开始指向Base2开头。
所以答案是 p1 == p3 != p2 
借上面那个题,说了多继承中的指针偏移问题。

4.继承和组合

  • public继承是⼀种is-a的关系。也就是说每个派⽣类对象都⼀个基类对象。
  • 组合是⼀种has-a的关系。假设B组合了A,每个B对象中都⼀个A对象。
  • 继承允许你根据基类的实现来定义派⽣类的实现。这种通过⽣成派⽣类的复⽤通常被称为⽩箱复⽤(white-box reuse)。在继承⽅式中,基类的内部细节对派⽣类可⻅ 。继承⼀定程度破坏了基类的封装,基类的改变,对派⽣类有很⼤的影响。派⽣类和基类间的依赖关系很强,耦合度⾼
  • 对象组合是类继承之外的另⼀种复⽤选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接⼝。这种复⽤⻛格被称为⿊箱复⽤(black-box reuse),因为对象的内部细节是不可⻅的。对象只以“⿊箱”的形式出现。 组合类之间没有很强的依赖关系,耦合度低
  • 优先使⽤组合,⽽不是继承。实际尽量多去⽤组合,组合的耦合度低,代码维护性好,但这也不是绝对的,要根据实际选择使用。

比如下面的代码,就能很好解释继承和组合。

class Tire //轮胎
{
protected:string _brand; // 品牌size_t _size; // 尺⼨
};
class Car //车
{
protected:string _colour; // 颜⾊string _num; // ⻋牌号Tire _t1; // 轮胎Tire _t2; // 轮胎Tire _t3; // 轮胎Tire _t4; // 轮胎
};

轮胎和车就比较符合 has-a 的关系,车轮胎,用的组合

class BMW : public Car //继承
{
public:void Drive() { cout << "BMW" << endl; }
};class Benz : public Car //继承
{
public:void Drive() { cout << "Benz" << endl; }
};

上面的代码是继承,BMW 和 Benz 是品牌,比较符合 is-a 的关系,BMW和Benz车。

本次分享就到这里了,我们下篇见~

相关文章:

【C++】详细讲解继承(下)

本篇来继续说说继承。上篇可移步至【C】详细讲解继承&#xff08;上&#xff09; 1.继承与友元 友元关系不能继承 &#xff0c;也就是说基类友元不能访问派⽣类私有和保护成员。 class Student;//前置声明class Same //基类 { public:friend void Fun(const Same& p, con…...

消息队列篇--原理篇--Pulsar(Namespace,BookKeeper,类似Kafka甚至更好的消息队列)

Apache Pulusar是一个分布式、多租户、高性能的发布/订阅&#xff08;Pub/Sub&#xff09;消息系统&#xff0c;最初由Yahoo开发并开源。它结合了Kafka和传统消息队列的优点&#xff0c;提供高吞吐量、低延迟、强一致性和可扩展的消息传递能力&#xff0c;适用于大规模分布式系…...

扬帆数据结构算法之舟,启航C++探索征途——LeetCode深度磨砺:顺序表技术精进实践

人无完人&#xff0c;持之以恒&#xff0c;方能见真我&#xff01;&#xff01;&#xff01; 共同进步&#xff01;&#xff01; 文章目录 顺序表练习1.移除数组中指定的元素方法1&#xff08;顺序表&#xff09;方法2&#xff08;双指针&#xff09; 2.删除有序数组中的重复项…...

基于本地事务表+MQ实现分布式事务

基于本地事务表MQ实现分布式事务 引言1、原理2、本地消息表优缺点3、代码实现3.1、代码执行流程3.2、项目结构3.3、项目源码 引言 本地消息表的方案最初由ebay的工程师提出&#xff0c;核心思想是将分布式事务拆分成本地事务进行处理。本地消息表实现最终一致性。本文主要学习…...

数据结构:二叉树—面试题(一)

目录 1、相同的树 2、另一棵树的子树 3、翻转二叉树 4、平衡二叉树 5、对称二叉树 6、二叉树遍历 7、二叉树的分层遍历 1、相同的树 习题链接https://leetcode.cn/problems/same-tree/description/https://leetcode.cn/problems/same-tree/description/ 描述&#xff1a…...

【Wordpress网站制作】切换语言的问题

前言 自学笔记&#xff0c;解决问题为主&#xff0c;欢迎补充。 本文重点&#xff1a;如何将页面语言从默认的【英语】修改成【中文】。 问题描述 安装完wordpress&#xff0c;在【Setting】→【General】的语言中&#xff0c;选项只有英语。无法切换成中文 方法1: 在 wp-c…...

【第二天】零基础入门刷题Python-算法篇-数据结构与算法的介绍-五种常见的排序算法(持续更新)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Python数据结构与算法的详细介绍1.Python中的常用的排序算法1.排序算法的介绍2.五种详细的排序算法代码 总结 前言 提示&#xff1a;这里可以添加本文要记…...

Neural networks 神经网络

发展时间线 基础概念 多层神经网络结构 神经网络中一个网络层的数学表达 TensorFlow实践 创建网络层 神经网络的创建、训练与推理 推理 推理可以理解为执行一次前向传播 前向传播 前向传播直观数学表达 前向传播直观数学表达的Python实现 前向传播向量化实现 相关数学知识…...

汽车免拆诊断案例 | 2007 款日产天籁车起步加速时偶尔抖动

故障现象  一辆2007款日产天籁车&#xff0c;搭载VQ23发动机&#xff08;气缸编号如图1所示&#xff0c;点火顺序为1-2-3-4-5-6&#xff09;&#xff0c;累计行驶里程约为21万km。车主反映&#xff0c;该车起步加速时偶尔抖动&#xff0c;且行驶中加速无力。 图1 VQ23发动机…...

代码随想录day3

203:移除链表元素&#xff1a;注意虚拟头节点的使用 ListNode* removeElements(ListNode* head, int val) {ListNode* result new ListNode();result->next head;ListNode* current result;while(current ! nullptr && current->next ! nullptr){if(current-…...

Spring 面试题【每日20道】【其一】

1、Spring 当中什么是循环依赖&#xff08;常问&#xff09;&#xff1f; 中等 在Spring框架中&#xff0c;循环依赖&#xff08;Circular Dependency&#xff09;是指两个或多个bean互相之间直接或间接地依赖对方的注入。例如&#xff1a; A bean依赖于B bean。B bean又依赖…...

leetcode刷题记录(八十九)——35. 搜索插入位置

&#xff08;一&#xff09;问题描述 35. 搜索插入位置 - 力扣&#xff08;LeetCode&#xff09;35. 搜索插入位置 - 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位…...

Flutter 与 React 前端框架对比:深入分析与实战示例

Flutter 与 React 前端框架对比&#xff1a;深入分析与实战示例 在现代前端开发中&#xff0c;Flutter 和 React 是两个非常流行的框架。Flutter 是 Google 推出的跨平台开发框架&#xff0c;支持从一个代码库生成 iOS、Android、Web 和桌面应用&#xff1b;React 则是 Facebo…...

基于Docker的Spark分布式集群

目录 1. 说明 2. 服务器规划 3. 步骤 3.1 要点 3.2 配置文件 3.2 访问Spark Master 4. 使用测试 5. 参考 1. 说明 以docker容器方式实现apache spark计算集群&#xff0c;能灵活的增减配置与worker数目。 2. 服务器规划 服务器 (1master, 3workers) ip开放端口备注ce…...

Web 代理、爬行器和爬虫

目录 Web 在线网页代理服务器的使用方法Web 在线网页代理服务器使用流程详解注意事项 Web 请求和响应中的代理方式Web 开发中的请求方法借助代理进行文件下载的示例 Web 服务器请求代理方式代理、网关和隧道的概念参考文献说明 爬虫的工作原理及案例网络爬虫概述爬虫工作原理 W…...

MySQL 事件调度器

MySQL 事件调度器确实是一个更方便且内置的解决方案&#xff0c;可以在 MySQL 服务器端自动定期执行表优化操作&#xff0c;无需依赖外部工具或应用程序代码。这种方式也能减少数据库维护的复杂性&#xff0c;尤其适用于在数据库频繁更新或删除时进行自动化优化。 使用 MySQL …...

直线拟合例子 ,岭回归拟合直线

目录 直线拟合,算出离群点 岭回归拟合直线&#xff1a; 直线拟合,算出离群点 import cv2 import numpy as np# 输入的点 points np.array([[51, 149],[122, 374],[225, 376],[340, 382],[463, 391],[535, 298],[596, 400],[689, 406],[821, 407] ], dtypenp.float32)# 使用…...

Flutter android debug 编译报错问题。插件编译报错

下面相关内容 都以 Mac 电脑为例子。 一、问题 起因&#xff1a;&#xff08;更新 Android studio 2024.2.2.13、 Flutter SDK 3.27.2&#xff09; 最近 2025年 1 月 左右&#xff0c;我更新了 Android studio 和 Flutter SDK 再运行就会出现下面的问题。当然 下面的提示只是其…...

关于IPD流程的学习理解和使用

IPD&#xff08;Integrated Product Development&#xff0c;集成产品开发&#xff09;是一种系统化的产品开发流程和方法论&#xff0c;旨在通过跨职能团队的协作和并行工程&#xff0c;缩短产品开发周期&#xff0c;提高产品质量&#xff0c;降低开发成本。IPD 最初由美国 PR…...

C# 类(Class)

C# 类&#xff08;Class&#xff09; 概述 在C#编程语言中&#xff0c;类&#xff08;Class&#xff09;是面向对象编程&#xff08;OOP&#xff09;的核心概念之一。类是一种用户定义的数据类型&#xff0c;它包含了一组属性&#xff08;数据&#xff09;和方法&#xff08;…...

Jenkins pipline怎么设置定时跑脚本

目录 示例&#xff1a;在Jenkins Pipeline中设置定时触发 使用pipeline指令设置定时触发 使用Declarative Pipeline设置定时触发 使用Scripted Pipeline设置定时触发 解释Cron表达式 保存和应用配置 小结 在Jenkins中&#xff0c;定时跑脚本&#xff08;例如定时执行Pip…...

PostgreSQL模糊查询相关学习参考

PostgreSQL大数据量快速模糊检索实践_postgresql 模糊查询-CSDN博客文章浏览阅读1.5k次&#xff0c;点赞20次&#xff0c;收藏25次。注意&#xff1a; 本文内容于 2024-08-18 23:50:33 创建&#xff0c;可能不会在此平台上进行更新。。_postgresql 模糊查询https://blog.csdn.n…...

【电脑无法通过鼠标和键盘唤醒应该怎么办】

【电脑无法通过鼠标和键盘唤醒应该怎么办】 方法一(有时候不起作用):方法二(方法一无效时,使用方法二): 方法一(有时候不起作用): 方法二(方法一无效时,使用方法二):...

Java 大视界 -- Java 大数据中的数据脱敏技术与合规实践(60)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

Vue3.5 企业级管理系统实战(三):页面布局及样式处理 (Scss UnoCSS )

本章主要是关于整体页面布局及样式处理&#xff0c;在进行这一章代码前&#xff0c;先将前两章中的示例代码部分删除&#xff08;如Home.vue、About.vue、counter.ts、App.vue中引用等&#xff09; 1 整体页面布局 页面整体布局构成了产品的框架基础&#xff0c;通常涵盖主导…...

【xcode 16.2】升级xcode后mac端flutter版的sentry报错

sentry_flutter 7.11.0 报错 3 errors in SentryCrashMonitor_CPPException with the errors No type named terminate_handler in namespace std (line 60) and No member named set_terminate in namespace std 替换sentry_flutter版本为&#xff1a; 8.3.0 从而保证oc的…...

windows在命令行中切换盘符

一、问题描述 我们在使用windows的cmd&#xff08;命令行&#xff09;时&#xff0c;经常需要用cd命令在不同盘之间切换路径。但有时在不同盘之间切换时&#xff0c;会发现命令不起作用。 如下图所示&#xff0c;直接切换目录还是停留在原来的位置。 二、解决方法 首先切换盘符…...

亚博microros小车-原生ubuntu支持系列:11手指控制与手势识别

识别框架还是沿用之前的了MediaPipe Hand。 背景知识不摘重复&#xff0c;参见之前的&#xff1a;亚博microros小车-原生ubuntu支持系列&#xff1a;10-画笔-CSDN博客 手指控制 src/yahboom_esp32_mediapipe/yahboom_esp32_mediapipe/目录下新建文件10_HandCtrl.py&#xff…...

JAVA-快速排序

目录 一、快速排序基本思想 二、快速排序的实现 1.Hoare法找基准值 2.挖坑法 3.前后指针法(了解) 三、快速排序的优化 1.三数取中法 2.递归到小的子区间时&#xff0c;可以考虑使用插入排序 四、非递归的写法 五、时间空间复杂度 一、快速排序基本思想 快速排序是 H…...

日志收集Day003

1.索引模板 查看所有索引模板 GET 10.0.0.101:9200/_template 2.查看单个索引模板 GET 10.0.0.101:9200/_template/.monitoring-es 3.创建索引模板 POST 10.0.0.101:9200/_template/lxctp {"aliases": {"DBA": {},"SRE": {},"K8S&qu…...