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

C++ 继承(二)

目录

1. 实现一个不能被继承的类

 2. 友元与继承

 3.继承与静态成员

4.多继承及其菱形继承问题

(1). 继承模型

(2). 虚继承

(2.1)虚继承解决数据冗余和二义性的原理

(3). 多继承中指针偏移问题

(4). IO库中的菱形虚拟继承

5. 继承和组合


1. 实现一个不能被继承的类

方法1:父类的构造函数私有,子类构成必须调用父类的构造函数,但是父类的构造函数私有后,子类就不能调用了。那子类将无法实例化处对象。

 如下代码所示

#include<iostream>
#include<algorithm>using namespace std;
class Teacher
{
private:Teacher(){x = 1;}int x;
};class Student: public Teacher
{
public:Student(){ss = 1;}
private:int ss;
};

 因为父类构造函数不能调用出错

 

方法2:C++11中新增了final关键字,在父类,类名后加上final修饰子类就不能继承了

 代码如下

#include<iostream>
#include<algorithm>using namespace std; 
class Teacher final
{
public:Teacher(){x = 1;}
private:int x;
};class Student: public Teacher
{
public:Student(){ss = 1;}
private:int ss;
};

 不可以将其当做基类(父类)

 2. 友元与继承

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

 代码如下

#include<iostream>
#include<algorithm>using namespace std; class Student;//提前声明否则友元函数定义找不到Student
class Teacher 
{public:friend void playval( const Teacher& t, const Student& s);Teacher(int xx=11){x = xx;}
private:
//protected:int x;
};class Student: public Teacher
{
public:Student():Teacher(11){ss = 1;}
private:int ss;
};void playval(const Teacher& t,const Student& s )
{cout << t.x << endl;cout << s.ss << endl;
}int main()
{Teacher t(16);Student s;playval(t, s);
}

结果如图,不能找到s的私有成员

 当然子类的友元也不能访问父类的保护和私有成员

 如下代码所示

#include<iostream>
#include<algorithm>using namespace std; class Teacher 
{public:Teacher(int xx=11){x = xx;}
private:
//protected:int x;
};class Student: public Teacher
{
public:friend void inputval(const Teacher& t, const Student& s);Student():Teacher(11){ss = 1;}
private:int ss;
};void inputval(const Teacher& t, const Student& s)
{cout << t.x << endl;cout << s.ss << endl;
}int main()
{Teacher t(16);Student s;inputval(t, s);return 0;
}

结果如下图所示子类的友元函数找不到其父类的私有与保护成员 

 3.继承与静态成员

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

 如下代码所示

#include<iostream>
using namespace std;class Teacher
{
public:string _name;static int _count;
};
int Teacher::_count = 0;
class Student : public Teacher
{
protected:int _stuNum;
};
int main()
{Teacher t;Student s;// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的 // 说明⼦类继承下来了,⽗⼦类对象各有⼀份 cout << &t._name << endl;cout << &s._name << endl;// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的 // 说明⼦类和⽗类共⽤同⼀份静态成员 cout << &t._count << endl;cout << &s._count << endl;// 公有的情况下,⽗⼦类指定类域都可以访问静态成员 cout << Teacher::_count << endl;cout << Student::_count << endl;return 0;
}

运行结果如下

我们可以看到子类对象中的_name与父类对象中的_name地址不同,而子类对象与父类对象的_count地址是相同的。说明父类对象与子类对象共用一个静态成员

4.多继承及其菱形继承问题

(1). 继承模型

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

 如下图

 

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承,多继承对象在内存中的模型是  先继承的父类在前面,后继承的父类在后面,子类成员在最后面

 如下图

在内存中的分布如下

菱形继承:菱形继承是多继承的一种特殊情况。菱形继承的问题,从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题,在Assistant的对象中Person成员会有两份。支持多继承就一定会有菱形继承,实践中我们尽量不设计出菱形继承这样的模型

 

 代码演示

#include<iostream>
#include<string>using namespace std; class Person
{
public:string _name; // 姓名 
};
class Student : public Person
{
protected:int _num; 
};
class Teacher : public Person
{
protected:int _id; 
};
class headmaster : public Student, public Teacher
{
protected:string _Course; 
};

上述代码即为一个菱形继承

int main()
{// 编译报错: 对“_name”的访问不明确 headmaster a;a._name = "peter";return 0;
}

这样调用编译会报错

编译器不知道调用的是哪个父类中的_name

我们可以通过显式指定访问那个父类的成员来解决二义性的问题,但是无法解决数据冗余的问题

int main()
{//a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}
(2). 虚继承

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

using namespace std;class Person
{
public:string _name; // 姓名 
};
class Student : virtual public Person
{
protected:int _num;
};
class Teacher : virtual public Person
{
protected:int _id;
};
class headmaster : public Student, public Teacher
{
protected:string _Course;
};int main()
{headmaster h;h._name = "lisi";cout << h._name << endl;h.Student::_name = "l";cout << h._name << endl;h.Teacher::_name = "s";cout << h._name << endl;cout << &h._name << endl;cout << &h.Student::_name << endl;cout << &h.Teacher::_name << endl;return 0;
}

输出结果如下

 可以看到这三个在内存中用了一个地址空间,这样解决了二义性和数据冗余的问题

(2.1)虚继承解决数据冗余和二义性的原理

如下代码

#include<iostream>
#include<string>using namespace std;class Person
{
public:int a;
};
class Student : virtual public Person
{
public:int _num;
};
class Teacher : virtual public Person
{
public:int _id;
};
class headmaster : public Student, public Teacher
{
public:int test;
};int main()
{headmaster h;h.Student::a = 1;h.Teacher::a = 2;h._num = 3;h._id = 4;h.test = 5;return 0;
}

 在内存中如下所示

我们上面提到过,多继承中,先继承的父类在前面后继承的父类在后面,子类成员放在最后面。我们这里可以看出headmaster对象中将Person放到了对象组成的最下面,这个Person同时属于Student和Teacher,那Student和Teacher如何去找到公共的Person呢?

这里是通过它们的两个指针指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表虚基表中存的偏移量。通过偏移量可以找到Person

 内存2是p1指向的地址,内存3是p2指向的地址它们下面指针的指向是相同的

Teacher和Student自己定义的对象也可以通过这样来找到Person

如下

int main()
{Teacher t;Student s;t.a = 1;cout << t.a << endl;s.a = 2;cout << t.a << endl;cout << s.a << endl;return 0;
}

结果为

(3). 多继承中指针偏移问题

关于下面程序说法正确的是

A: p1==p2==p3  B: p1<p2<p3  C: P1==p3!=p2  D: p1!=p2!=p3

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;
}

先继承的父类在前面,后继承的父类在后面,子类成员在最后面,所以p1与p3指向相同

选C

(4). IO库中的菱形虚拟继承

template<class CharT, class Traits = std::char_traits<CharT>>
class basic_ostream : virtual public std::basic_ios<CharT, Traits>
{};
template<class CharT, class Traits = std::char_traits<CharT>>
class basic_istream : virtual public std::basic_ios<CharT, Traits>
{};

5. 继承和组合

1. public继承是一种is-a的关系。也就是说每个子类对象都是一个父类对象

2. 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象

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

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

5. 优先使用组合,而不是继承。实际尽量多去用组合,组合的耦合度低,代码维护性好。不过也不那么绝对,类之间的关系适合继承(is-a)那就用继承,另外要实现多态也必须要继承。类之间的关系既适合用继承(is-a)也适合用组合(has-a),那就用组合

6. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂,性能也会由一些损失,所以最好不要设计出菱形继承。多继承可以认为是C++的缺陷之一,后来的一些编程语言都没有多继承比如Java

例如汽车类(car) 和 轮胎类(tire) 适合使用组合方式实现,动物类可以作为狗类的父类(继承思想)


这篇就到这里啦(づ ̄3 ̄)づ╭❤~

相关文章:

C++ 继承(二)

目录 1. 实现一个不能被继承的类 2. 友元与继承 3.继承与静态成员 4.多继承及其菱形继承问题 (1). 继承模型 (2). 虚继承 (2.1)虚继承解决数据冗余和二义性的原理 (3). 多继承中指针偏移问题 (4). IO库中的菱形虚拟继承 5. 继承和组合 1. 实现一个不能被继承的类 方法1…...

第 2 章:AJAX 的使用

AJAX 的使用 核心对象&#xff1a;XMLHttpRequest&#xff0c;AJAX 的所有操作都是通过该对象进行的。 1. 使用步骤 创建 XMLHttpRequest 对象 var xhr new XMLHttpRequest(); 设置请求信息 xhr.open(method, url);//可以设置请求头&#xff0c;一般不设置 xhr.setReques…...

ROS——视觉抓取

纲要 视觉抓取中的关键技术 内参标定 物体识别定位 抓取姿态分析 运动规划 外参标定 任意两个位姿之间的关系 眼在外 眼在内 手眼标定流程 robot 部分 标定效果 视觉抓取例程 grasping_demo.cpp 获取两个坐标系之间变换关系:waitForTransform 、 LookupTransform 求相…...

EPLAN2022基础教程

EPLAN2022软件介绍 EPLAN是一款专业的电气设计和绘图软件&#xff0c;它可以帮助我创建和管理电气项目&#xff0c;生成各种报表和文档&#xff0c;与其他软件和系统进行交互&#xff0c;优化工程流程和质量。与传统的CAD绘图对比&#xff0c;EPLAN更适合绘制电气原理图。 下…...

【JavaWeb】Servlet 详解(处理逻辑及常见方法)

文章目录 1. Tomcat1.1 常见的错误1.1.1 出现 4041.1.2 出现 4051.1.3 出现 500 1.2 HttpServlet1.2.1 Tomcat 的处理逻辑1.2.2 相关方法 1.3 HttpServletRequest1.3.1 常见方法1.3.2 jackson 处理逻辑 1.4 HttpServletResponse1.4.1 常见方法 1. Tomcat tomcat 是一个 HTTP 服…...

6 自研rgbd相机基于rk3566之深度计算库程序详解

自研rgbd相机基于rk3566之深度计算库详解 1 tof深度计算库框架读入深度图像参数配置tof模组标定参数读入及解析深度计算函数接口2 tof深度计算库程序详解深度计算程序头文件深度计算程序 源文件1 tof深度计算库框架 读入深度图像参数配置 支持raw8/raw10/raw16 格式 /*******…...

分布式系统框架hadoop3入门

分布式系统框架hadoop3入门 (qq.com) Hadoop3作为分布式系统架构的重要基石&#xff0c;为大规模数据存储与处理提供了强大支持 基本信息 hadoop&#xff1a;一个存储和处理大数据的分布式系统框架 组成&#xff1a; HDFS&#xff08;数据存储&#xff09;、MapReduce&…...

使用 i3.LayoutCell() 方法绘制版图并输出为 GDS 文件

使用 i3.LayoutCell 方法绘制版图并输出为 GDS 文件 引言正文引言 在 IPKISS i3.SRef() 函数 一文中我们介绍了如何使用 i3.SRef() 函数将 instance 对象添加到 i3.LayoutCell() 创建的 Cell 对象上。但是当我们使用 write_gdsii() 输出版图时代码就会报错。这里我们将介绍如何…...

mariadb容器

下载镜像 $ sudo docker pull mariadb启动容器 $ sudo docker run --name my-mariadb -d -e MARIADB_DATABASEtestdb -e MARIADB_ROOT_PASSWORDLetmein -p 3306:3306 mariadb上面命令会启动一个名为my-mariadb的容器&#xff0c;并初始化一个testdb数据库&#xff0c;同时设置…...

应用层协议Http

Http协议 1.1 什么是http协议 在进行网络通信时&#xff0c;应用层协议一般都是程序员自己写的&#xff0c;但是有一些大佬其实已经定义出了一些现成的应用层协议&#xff0c;例如&#xff1a;HTTP&#xff08;超文本传输协议&#xff09;、FTP&#xff08;文件传输协议&#…...

display flex 的div 被子元素撑开不显示滚动条的一个解决demo

display flex 的div 被子元素撑开&#xff0c;不显示y轴滚动条的 一个解决demo。 注&#xff1a; 不一定适用所有人的的相同问题 less # less .contact {display: flex;flex-grow: 1;overflow: hidden auto;flex-direction: column;.contact-items {flex: 1 1 0;display: flex…...

判断键盘输入是数字、大写字母还是小写字母——C#学习笔记

以下代码将判断键盘输入字符是数字 还是字母&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApp4 {using System;using System.Threading;public class BoolKeyTest{sta…...

进程控制块PCB的组织方式有哪些?

进程控制块&#xff08;PCB&#xff0c;Process Control Block&#xff09;是操作系统用来管理和跟踪进程的一个数据结构&#xff0c;它保存了与进程相关的各种信息。PCB 是操作系统调度进程的核心数据结构&#xff0c;通常通过某种组织方式进行管理。常见的 PCB 组织方式主要有…...

getent passwd 获取linux并显示用户账户信息

getent passwd 命令在Unix和类Unix系统&#xff08;如Linux&#xff09;中用于从系统的密码数据库&#xff08;通常是/etc/passwd文件&#xff0c;但在某些配置中可能是通过网络服务如NIS或LDAP&#xff09;中获取并显示用户账户信息。这个命令的输出列出了系统上所有用户的详细…...

达梦数据库+JPA+Springboot 报错 :无效的列名

文章目录 0、 开发环境1、需求说明2、适配过程3、问题描述和解决3.1报错问题3.2 问题解决 0、 开发环境 开发环境&#xff1a; idea 2022 SpringBoot版本 &#xff1a;2.7.1 JDK 111、需求说明 适配国产化数据库达梦8 2、适配过程 参考 Springboot jpa 达梦 3、问题描述…...

#单片机基础 笔记一

--单片机概述STM32开发环境搭建创建工程 --STM32-GPIO&#xff08;重点&#xff09;编程 --STM32-USART串口应用 --STM32-SPI(液晶屏)中断系统 --STM32-时钟系统&#xff08;重点中的重点&#xff09; --STM32-PWMADC --STM32-DMA DHT11 1. 计算机组成原理 1.1 计算机的组成 1…...

echarts多个环形图

echarts图表集 var dataValue [{name:今日待分配方量,value:49}, {name:今日已分配方量,value:602}, {name:今日完成方量,value:1037}]var piedata1 [{name: 1#拌和机,value: 20},{name: 2#拌和机,value: 22},{name: 3#拌和机 ,value: 17},{name: 4#拌和机,value: 18},{name…...

vue 的面试题

一、Html篇 1、常用的块级元素及行内元素有哪些&#xff1f; 块级元素&#xff1a;div、p、h1~h6、ol、ul、li、table、form 行内标签&#xff1a;a、span、img、input、lable、button 行内块元素&#xff1a;img、input、button 2、行内元素和块级元素的区别&#xff1f; 块级…...

MongoDB-部署PSA(一主一从一仲裁)架构复制集群

目录 环境信息环境准备mongoDB配置&部署复制集群搭建 环境信息 IP端口节点10.0.0.127017主10.0.0.227017从10.0.0.327017仲裁 环境准备 1.关闭THP Transparent Huge Pages 简称 THP。透明大页面&#xff08;THP&#xff09;是一种Linux内存管理系统&#xff0c;通过使用…...

CSS中 特殊类型的选择器 伪元素如何使用

一、什么是伪元素 在 CSS 中&#xff0c;伪元素是一种特殊类型的选择器&#xff0c;它允许你为元素的特定部分添加样式&#xff0c;而这些部分在 HTML 文档中并不实际存在。伪元素通常用于创建装饰性效果&#xff0c;如添加边框、背景、阴影等&#xff0c;而不需要额外的 HTML…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建

【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...

Linux中《基础IO》详细介绍

目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改&#xff0c;实现简单cat命令 输出信息到显示器&#xff0c;你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...