C++ 继承(1)

1.继承概念

我们平时有时候在写多个有内容重复的类的时候会很麻烦

比如我要写Student Teacher Staff 这三个类

里面都要包含  sex  name age成员变量

唯一不同的可能有一个成员变量 但是这三个成员变量我要写三遍  

太麻烦了 有没有好的方式呢?

有的 就是继承、我们先来看继承的书写格式

但是我们继承过来的类里面的成员是public  private 还是protected呢?

在此之前我们要了解一下继承方式和访问限定符有哪些

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

这个地方很好记忆 记住两点就可以了

 ***这里的不可见要和private区分开来

***简单点说就是基类访问方式和派生类的继承方式的最小方式 

除此之外还有我们之前学类成员函数没有了解到的protected

 虽然struct和class有默认继承方式,但是最好还是显示写

当然以上这么多看起来好麻烦 但是实际让我们用的很少

2.基类和派生类类型转换

基类赋值给派生类是向下转换

派生类赋值给基类是向上转换

1.对于类成员,首先基类不能赋值给派生类(强制类型转换也不可以)

因为派生类有基类没有的成员变量 怎么赋值 

2.此外派生类赋值给基类时,既不是隐式类型转换 也不是强制类型转换

而是赋值兼容(也称切割,切片) 因此不会产生临时变量 不具有常性

把派生类中的基类部分切割出来 赋值给基类

class Person
{};class Student :public Person
{};int main(void)
{int i = 0;double& d = i;//隐式类型会产生临时变量 具有常性 报错Person p;Student s;p = s;Person& pp = s;//赋值兼容 不产生临时变量 不具有常性 不报错return 0;
}

像上面代码的变量pp 它是把派生类中的基类部分切割出来取别名

像上转换都是可以的

派生类赋值给 基类对象/引用/指针

至于向下转换

对象是不支持的

但是指针和引用可以

基类的指针/引用可以指向派生类中基类的那一部分

后面再详细讲解 

3.继承中的作用域

1.在继承体系中 基类和派生类都有独立的作用域

那么基类和派生类如果有同名的成员呢?

当然是可以 在不同的作用域里面可以有同名的函数

就像stack里面有push

queue里面也有push啊!

C++对于这种同名的成员变量或者函数取了一个名称 叫隐藏也叫重定义

这个地方函数的同名要和函数重载区分一下

首先函数重载是在同一个域中

这个地方的函数同名是处于两个不同的域

我们直接上代码就很容易理解了

class Person
{
public:void func(){cout << "基类" << endl;}
};class Student :public Person
{
public:void func(int){cout << "派生类" << endl;}
};
int main(void)
{Person p;Student s;Person& pp = s;s.func();//错误s.func(2);//正确s.Person::func();//正确return 0;
}

只要派生类和基类函数名相同就构成隐藏

总结

4.派生类的默认成员函数 

我们的派生类变量   中基类变量那部分   初始化会调用基类的构造函数

不可以在  派生类的初始化列表初始化   但是可以在派生类的构造函数函数体内使用

class Person
{
public:Person(int a){cout << "基类" << endl;}
public:string sex;
};class Student :public Person
{
public:Student():age(18),Person(2)//不加就会报错{sex = 12;cout << sex << endl;}
private:int age = 18;
};
int main(void)
{Student s;return 0;
}

 不加,Person(2)这个地方报错

这个地方你会发现先初始化Person后初始化age 因为初始化的顺序不是按照初始化列表的顺序

而是按照声明的顺序

按照声明来说  继承的类是在派生类自己类变量前面的

析构函数,拷贝构造和赋值重载也是差不多 但是还是有一些细微的差别

本质上相同!

​
​
class Person
{
public:Person(string a="Tony"){cout << "基类" << endl;}Person(const  Person& v):sex(v.sex){}Person& operator=(Person& v){if (this != &v){v.sex = sex;}return*this;}~Person(){}
public:string sex;
};class Student :public Person
{
public:Student(string sex="zhangsan1"):age(18),Person(sex){}Student(const Student&s):age(s.age),Person(s)//Person拷贝构造函数要求Person类型的对象//这个地方我们没有Person对象  但是也可以直接传//派生类对象变量可以传给基类对象(向上转换 切割)////这个地方Person(s)你不写编译器就会去调用Person的默认构造函数{}Student& operator=(Student& v){if (this != &v){Person::operator=(v);//这个地方就涉及到我们前面学习的隐藏了age = v.age;}return *this;}~Student(){//Person:~Person	由于后面多态的原因,析构函数的函数名被特殊处理了,// 统一处理成destructor了 这个时候就构成隐藏了//但是实际上这个地方不用我们显示写 因为编译器会自动调用//这个地方是先析构派生类自己的成员 再去析构派生类中基类的那部分成员//原因是派生类中可能会用到基类的成员  // 比如cout<<sex<<endl;}
private:int age = 18;
};​​

5.继承与友元

很简单 一句话

派生类不能继承基类的友元

6.继承与静态成员 

我们来看这一串代码

打印的地址相同说明派生类和基类使用的是同一个 

这个地方的静态成员属于父类和派生类

在派生类中不会单独拷贝一份、继承的是使用权

当然我们使用静态变量 无论是域名或者对象都可以! 

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

继承是可以多继承的

但是多继承就容易出现问题

比如菱形继承

 这个时候就会出现两个问题

1.冗余性

我把同样的成员变量拷贝了两份,太冗余了

2.二义性

比如上图的Student类和Teacher类都要age成员的情况下

Assitant a;  a.age的这个age是从Teacher类继承过来的还是从Student继承过来的?

首先  解决二重性的方式比较简单 

我要用Tearher继承来的age就直接 a.Teacher::age;

Tearher继承来的age就直接 a.Student::age;

那么怎么解决冗余性呢?

这就涉及到了虚拟菱形继承了

虚拟菱形继承就是在普通继承前面+一个virtual!

class D : public virtual B, public virtual C
{
}

我们先来看普通菱形继承是怎么样的?

普通的菱形继承就和我们前面说的一样

有冗余 A类的成员被储存了两次 

我们再来看菱形虚拟继承

它把原本B类和C类共有继承的A类 单独 拿出来了 

但是这个地方多了两个东西 多了两个指针

指向的内容是距离A的偏移量(相对距离)

这也就意味着我们在使用B类和C类指针的时候

可以根据指针找到偏移量找到A的位置 毕竟A是B和C的基类

但是这个时候它们就没有存放到B和C里面

那么我为为什么不直接存偏移量在B和C里面 要存一个指针去找呢?

我们看到那个偏移量是存在第二个位置

第一个位置是为其他的预留位置

而且万一是三个值 四个值呢?

那我不是要存三四个地址?

那B C的大小会变得很大

而且万一你有其他D对象呢

那你岂不是要把所有的内容再存一遍?

直接用指针指向同一块公共区域不香吗?

毕竟这个偏移量只要是D对象都是固定的啊!

 这个时候B类的结构也发生了变化

偏移量和在D类对象的不一样

这个时候就容易出问题 尤其是切片的时候

我们再来看下面这串代码 

编译器单ptr->指向是无法判断ptr指向的是B类型的_a++还是D类型的

这个时候编译器就只能通过指针取到偏移量再根据偏移量去计算_aa的位置

上面两个ptr->_a++;本质上在编译的时候都是一样的

切片不需要特殊处理,都是通过指针去取偏移量 

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

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

相关文章

生成式聊天机器人 -- 基于Pytorch + Global Attention + 双向 GRU 实现的SeqToSeq模型 -- 下

生成式聊天机器人 -- 基于Pytorch Global Attention 双向 GRU 实现的SeqToSeq模型 -- 下 训练Masked 损失单次训练过程迭代训练过程 测试贪心解码(Greedy decoding)算法实现对话函数 训练和测试模型完整代码 生成式聊天机器人 – 基于Pytorch Global Attention 双向 GRU 实…

《ARM64体系结构编程与实践》学习笔记(四)

MMU内存管理 1.MMU内存管理&#xff08;armv8.6手册的D5章节&#xff09;&#xff0c;MMU包含快表TLB&#xff0c;TLB是对页表的部分缓存&#xff0c;页表是存放在内存里面的。 AArch64仅仅支持Long Descriptor的页表格式&#xff0c;AArch32支持两种页表格式Armv7-A Short De…

如何在Vscode中接入Deepseek

一、获取Deepseek APIKEY 首先&#xff0c;登录Deepseek官网的开放平台&#xff1a;DeepSeek 选择API开放平台&#xff0c;然后登录Deepseek后台。 点击左侧菜单栏“API keys”&#xff0c;并创建API key。 需要注意的是&#xff0c;生成API key复制保存到本地&#xff0c;丢失…

Docker 部署 MinIO | 国内阿里镜像

一、导读 Minio 是个基于 Golang 编写的开源对象存储套件&#xff0c;基于Apache License v2.0开源协议&#xff0c;虽然轻量&#xff0c;却拥有着不错的性能。它兼容亚马逊S3云存储服务接口。可以很简单的和其他应用结合使用&#xff0c;例如 NodeJS、Redis、MySQL等。 二、…

DeepSeek-R1 32B Windows+docker本地部署

最近国产大模型DeepSeek兴起&#xff0c;本地部署了一套deepseek同时集成Open WebUI界面,给大家出一期教程。 软件&#xff1a;Ollama、docker、Open WebUI 一、用Ollama下载模型 首先我们需要安装Ollama&#xff0c;它可以在本地运行和管理大模型。 到Ollama官网 https://ol…

TCP服务器与客户端搭建

一、思维导图 二、给代码添加链表 【server.c】 #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <fcntl.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <string.…

python爬虫--简单登录

1&#xff0c;使用flask框架搭建一个简易网站 后端代码app.py from flask import Flask, render_template, request, redirect, url_for, sessionapp Flask(__name__) app.secret_key 123456789 # 用于加密会话数据# 模拟用户数据库 users {user1: {password: password1}…

ESXi Host Client创建ubuntu虚拟机教程及NVIDIA显卡驱动安装

参考文章 VMware虚拟机显卡直通记录 AIGC 实战&#xff08;环境篇&#xff09; - EXSI 8.0 Debian安装RTX3060显卡驱动 重点介绍 client版本是7.0.3 注意&#xff1a;下图中不要选择BIOS 按照两个链接中的方法进行操作&#xff0c;以及本章节的上面几个图片的配置之后&a…

Maven入门核心知识点总结

Maven 1. POM&#xff08;Project Object Model&#xff09;2. 坐标&#xff08;Coordinates&#xff09;3. 依赖管理&#xff08;Dependency Management&#xff09;4. 常用五个生命周期&#xff08;Life Circle&#xff09;5. Maven 仓库&#xff08;Maven Repository&#x…

测试中的第一性原理:回归本质的质量思维革命

在软件工程领域&#xff0c;测试活动常被惯性思维和经验主义所主导——测试用例库无限膨胀、自动化脚本维护成本居高不下、测试策略与业务目标渐行渐远。要突破这种困境&#xff0c;第一性原理&#xff08;First Principles Thinking&#xff09;提供了独特的解题视角&#xff…

Rust语言进阶之标准输入: stdin用法实例(一百零五)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

SpringBoot速成(七)注册实战P2-P4

1.创建 数据库创建 依赖引入 <!-- mybatis起步依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version></dependency> <…

Spring Boot接入Deep Seek的API

1&#xff0c;首先进入deepseek的官网&#xff1a;DeepSeek | 深度求索&#xff0c;单击右上角的API开放平台。 2&#xff0c;单击API keys&#xff0c;创建一个API&#xff0c;创建完成务必复制&#xff01;&#xff01;不然关掉之后会看不看api key&#xff01;&#xff01;&…

【C++学习篇】C++11第二期学习

目录 1. 可变参数模板 1.1 基本语法及原理 1.2 包扩展 1.3empalce系列接⼝ 2. lamba 2.1 lambda的语法表达式 2.2 捕捉列表 2.3 lamba的原理 1. 可变参数模板 1.1 基本语法及原理 1. C11⽀持可变参数模板&#xff0c;也就是说⽀持可变数量参数的函数模板和类模板&…

开放式TCP/IP通信

一、1200和1200之间的开放式TCP/IP通讯 第一步&#xff1a;组态1214CPU&#xff0c;勾选时钟存储器 第二步&#xff1a;防护与安全里面连接机制勾选允许PUT/GET访问 第三步&#xff1a;添加PLC 第四步&#xff1a;点击网络试图&#xff0c;选中网口&#xff0c;把两个PLC连接起…

迁移学习 Transfer Learning

迁移学习&#xff08;Transfer Learning&#xff09;是什么&#xff1f; 迁移学习是一种机器学习方法&#xff0c;它的核心思想是利用已有模型的知识来帮助新的任务或数据集进行学习&#xff0c;从而减少训练数据的需求、加快训练速度&#xff0c;并提升模型性能。 &#x1f…

爬虫技巧汇总

一、UA大列表 USER_AGENT_LIST 是一个包含多个用户代理字符串的列表&#xff0c;用于模拟不同浏览器和设备的请求。以下是一些常见的用户代理字符串&#xff1a; USER_AGENT_LIST [Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; Hot Lingo 2.0),Mozilla…

我们来学人工智能 -- 将Ollama已下载的模型从C盘迁出

题记 未配置OLLAMA_MODELS系统变量导致模型下载到了C盘 迁移步骤 退出ollama 配置OLLAMA_MODELS系统变量 OLLAMA_MODELS&#xff1a;D:\ollama\models 直接将C盘下的models目录剪切到指定目录 检查 cmd命令窗口退出重新打开

Redis 集群原理、主从复制和哨兵模式的详细讲解

引言&#xff1a;本文记录了博主在学习Redis的过程中的原理&#xff0c;了解为什么使用与怎么样使用 Redis 集群&#xff0c;在使用 Redis 集群时出现的主从复制和哨兵模式的相关知识。本文并不涉及Redis安装。 文章目录 一、简单介绍什么是 Redis二、为什么要使用 Redis 集群三…

Java数据结构 | TreeMap 和 TreeSet

TreeMap 和 TreeSet 1. 搜索树1.1 概念1.2 搜索树的查找、插入、删除思路及代码1.2.1 查找1.2.2 插入1.2.3 删除&#xff08;难点&#xff09; 1.3 二叉搜索树的性能分析 2. Map 和 Set2.1 Map 接口2.1.1 Map.Entry<K,V>2.1.2 Map的常用方法 2.2 Set 接口2.2.1 Set 的常用…