C++(进阶) 第1章 继承

C++(进阶) 第1章 继承


文章目录

  • 前言
  • 一、继承
    • 1.什么是继承
    • 2.继承的使用
  • 二、继承方式
    • 1.private成员变量的(3种继承方式)继承
    • 2. private继承方式
    • 3.继承基类成员访问⽅式的变化
  • 三、基类和派生类间的转换
    • 1.切片
  • 四、 继承中的作⽤域
    • 1.隐藏规则:
    • 2.考察继承作⽤域相关选择题
  • 五、派⽣类的默认成员函数
    • 1.默认成员函数
    • 2.派生类的默认成员函数
      • 1.构造函数
      • 2.析构函数
      • 3.拷贝构造
      • 4.赋值
  • 总结


前言

在初级篇提过面向对象的三大特性:封装继承多态,在初阶篇可以非常直观的感受到封装是什么那么继承到底是什么呢?


一、继承

1.什么是继承

继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段,它允许我们在保持原有类特性的基础上进⾏扩展,增加⽅法(成员函数)和属性(成员变量),这样产⽣新的类,称派⽣类。继承呈现了⾯向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触函数层次的复⽤,继承是类设计层次的复⽤。


举个例子:
假如我现在有三个类分别是:骑手 商家 用户 ,这三个类都有下面这些基础信息,这些基础信息太过于冗余重复,这个时候就可以定义一个公共类然后让这三个类去继承
在这里插入图片描述


就像这样
在这里插入图片描述

2.继承的使用

class person
{
public:void print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "张三";int _age = 18;
};
class Student: public  person
{
protected:int _stuid;
};

假如现在有上面这俩个类,可以看到student
类里面是没有print函数的
在这里插入图片描述
可以看到这里s也是可以去调用print函数的


假如现在我们成员变量修改成public
在这里插入图片描述

在这里插入图片描述
这里可以看到俩个_name并不是一个,但是这里的成员函数是一个,可见父类和子类并不是同一个成员

但是成员函数是同一个,因为函数并不是存在一个对象里面,他们都是公用的

总结:继承本质上其实也是一种复用

二、继承方式

在这里插入图片描述
继承方式有三种和类里面的访问限定符是一样的


他们之间可以这样9组组合
在这里插入图片描述


这里可以把这表格堪称俩个部分
在这里插入图片描述
下面这个蓝色的可以看见都是不可见的,那么不可见是什么意思呢?


1.private成员变量的(3种继承方式)继承

private三种继承方式都是不可见这里只演示一个

class person
{
public:void print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
//private:string _name = "张三";int _age = 18;
};
class Student: public  person
{
public:void func(){cout << _age << endl;}
protected:int _stuid;
};
int main()
{Student s;s.func();return 0;
}

比如说上面的代码,是可见的,运行以后就是这样,可以去访问
在这里插入图片描述


现在我把private加上,这里就不让继承的类用了,这个就叫不可见
在这里插入图片描述


这里有一个容易混淆的概念,不可见不代表没有继承
在这里插入图片描述

可以看到上面还是给继承了的,但是就是无法直接调用,但是这里可以简直的去调用它,这里就可以直接去调用父类的成员函数实现间接的去调用
在这里插入图片描述


2. private继承方式

在1的代码的基础上,我把public的基础方式改成private
在这里插入图片描述

在这里插入图片描述

可以看到原本的public的成员函数也会变成private

3.继承基类成员访问⽅式的变化

通过上面的例子可以发现访问权限和继承权限他们是取权限小的那个
public < protected < private

现在在回头看原来的那个表格
在这里插入图片描述
可以看到最后的结果全部都是取到了最小的权限哪里


三、基类和派生类间的转换

1.切片

现在假如有一个父类和一个子类

class person
{
public:void print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "张三";int _age = 18;};
class Student: public person
{protected:int _stuid;
};

现在思考这样几个问题

  1. 能否把子类赋值给父类呢?
  2. 子类的指针能否赋值给父类?
  3. 子类的引用能否赋值给父类?

在这里插入图片描述
答案是可以的

这里引入一个新的概念赋值兼容转换


为了方便理解可以看下面这个图片
在这里插入图片描述
一般情况下父类的东西会比子类的少,因为父类一般都是放基本信息的,子类一般都是继承了父类信息的基础上在添加了一些东西就会像上面这样

#define _CRT_SECURE_NO_WARNINGS 
#include<bits/stdc++.h>
using namespace std;class person
{
public:void print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}string _name = "张三";
protected:int _age = 18;};
class Student: public person
{protected:int _stuid;
};
int main()
{person p;Student s;p = s;person* ptr = &s;person& ref = s;return 0;
}

现在我把保护去掉

可以发现上面的代码是没有报错的

值得注意的是这里并不是之前说的隐式类型转换这里是一个新的概念赋值兼容转换
在这里插入图片描述
在这里插入图片描述


如果子类赋值给父类那么编译器就会走特殊处理,他并不是传统类型的转换 ,但是这里父类不能给子类赋值


四、 继承中的作⽤域

1.隐藏规则:

  1. 在继承体系中基类和派⽣类都有独⽴的作⽤域。
  2. 派⽣类和基类中有同名成员,派⽣类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。(在派⽣类成员函数中,可以使⽤ 基类::基类成员 显⽰访问)
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成藏。
  4. 注意在实际中在继承体系⾥⾯最好不要定义同名的成员。

假如现在我代码是这样的

class person
{
public:void print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:int _age = 18;string _name = "张三";};
class Student: public person
{protected:string _name = "李四";int _stuid;
};

我这里student继承了父类,那么子类其实这里的函数并不会直接拷贝过来,他们其实还是共用一个那么,这里再用子类去调用print函数,假如这个时候刚好子类和父类里面刚好有同名变量那么这个print函数会去调用那个呢?

首先这样写并不会报错这里会按照就近原则去查找
在这里插入图片描述

2.考察继承作⽤域相关选择题

class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){cout << "func(int i)" <<i<<endl;}
};
int main()
{B b;b.fun(10);b.fun();return 0;
};
1. A和B类中的两个func构成什么关系(B)
A. 重载 B. 隐藏 C.没关系2.下⾯程序的编译运⾏结果是什么(A)
A. 编译报错 B. 运⾏报错 C. 正常运⾏

五、派⽣类的默认成员函数

首先第一个问题:父类的构造函数子类能不能用?答案是:能

1.默认成员函数

在这里插入图片描述
这里就只考虑前四个后面俩个没有那么重要


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

1.构造函数

假如现在这么一个代码
父类:

class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name;
};

派生类:

class Studen :public Person
{
protected:int _x;string _addrss;
};

派生类在原来的基础上在加俩个成员变量一个内置类型一个自定义类型

派生类的默认成员函数可以这样看

  1. 父类成员(整体)
  2. 子类自己的自定义类型
  3. 子类自己的内置类型
    只会调用默认构造函数

有变化的就只有1 下面的俩个都和普通的类一样


可以看到这里定义了一个子类,但是这里调用的是一个父类的构造和析构

在这里插入图片描述


从上面代码可以发现子类是会继承父类的构造函数和析构函数的


走完上面的1就直接去走23,内置初始化成随机值(不同编译器处理方式不一样)自定义就去找自定义的处理方式

值得注意的是这里只会调用默认构造带参数的构造是不会掉用的

在这里插入图片描述


假如说我现在要显示的写这个构造函数

在这里插入图片描述

它是不可以这么写的,这里报错是因为_name 是父类的成员函数
这里是编译器的规定记住就行了


假如说这里非要去显示调用构造函数这里就要这么写
在这里插入图片描述

在这里插入图片描述


2.析构函数

如果不写析构函数系统就会生成默认的析构函数,但是如果要显示的写
但是这里有这一个问题这里根本就编译不过

由于多态的原因析构函数会被编译器统一处理成destructorr() ,但是如果他们俩个都叫这个那么这里就会构成隐藏,所以这里连续调用了俩次就会报错

下面全部都是错误演示
在这里插入图片描述所以这里要指定作用域在这里插入图片描述


但是过了以后这里又开始扯淡了这里直接调用了这么多
在这里插入图片描述


这里可以发现多调用了,这里不得不说一个机制就是这里编译器做了处理子类析构函数里面会自动调用父类的析构,这里就是析构特殊的地方,其他几个都是显示切片调用就只有它是隐藏


3.拷贝构造

如果我们在子类里面不写拷贝构造那么编译器就会去调用父类的拷贝构造
在这里插入图片描述
所以一般情况下子类不需要写拷贝构造除非是设计到深拷贝


子类的就要这么写,子类调用拷贝构造就要去调用父类的拷贝构造就要这样去切片,因为这里要传一个父类过去但是我们没有所以我们这里就要用到上面说的复制兼容转换转一个自己(子类)编译器就会自己去切割
在这里插入图片描述

4.赋值

这里就是一个典型的隐藏错误
这里我是要子类切片去调用父类的拷贝,然后这里触发了上面说的隐藏,这里变成了自己调用自己,所以这里要指定作用域

Studen& operator=(const Studen& st)
{if (this != &st){operator=(st);_x = st._x;_addrss = st._addrss;}return *this;
}
Studen& operator=(const Studen& st)
{if (this != &st){Person:: operator=(st);_x = st._x;_addrss = st._addrss;}return *this;
}

在这里插入图片描述



总结

构造的顺序是先父后子,析构的顺序是先子后父,这里编译器做了强制处理

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

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

相关文章

Load-Balanced-Online-OJ(负载均衡式在线OJ)

负载均衡式在线OJ 前言1. 项目介绍2. 所用技术与环境所用技术栈开发环境 3. 项目宏观结构3.1 项目核心模块3.2 项目的宏观结构 4. comm公共模块4.1 日志&#xff08;log.hpp &#xff09;4.1.1 日志主要内容4.1.2 日志使用方式4.1.2 日志代码 4.2 工具&#xff08;util.hpp&…

c++->内部类 匿名对象

内部类&#xff1a;&#xff08;例如&#xff1a;b定义在a类中&#xff09; 注意事项&#xff1a; &#xff08;1&#xff09;内部类b可以直接使用外部类的static变量&#xff0c;但是并不属于外部类的友元&#xff01;&#xff01;&#xff01;&#xff01; #include <s…

C++ std::unique_ptr的使用及源码分析

目录 1.简介 2.使用方法 2.1.创建 unique_ptr 2.2.删除对象 2.3.转移所有权 2.4.自定义删除器 2.5.从函数返回 std::unique_ptr 2.6.将 std::unique_ptr 作为函数参数 3.适用场景 4.与原始指针的区别 5.优缺点 6.源码分析 6.1.构造函数 6.2.存储分析 6.3.默认删…

系统思考—关键决策

最近听到一句话特别扎心&#xff1a;“不是环境毁了企业&#xff0c;而是企业误判了环境。” 在大环境变化面前&#xff0c;很多企业的反应是快速调整&#xff0c;但这真的有效吗&#xff1f;其实&#xff0c;太快的动作&#xff0c;往往是误判的开始。 环境变化带来压力&…

【Java 解释器模式】实现高扩展性的医学专家诊断规则引擎

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

ES八股相关知识

为什么要使用ElasticSearch&#xff1f;和传统关系数据库&#xff08;如 MySQL&#xff09;有什么不同&#xff1f; 典型回答 数据模型 Elasticsearch 是基于文档的搜索引擎&#xff0c;它使用 JSON 文档来存储数据。在 Elasticsearch 中&#xff0c;相关的数据通常存储在同…

局域网与广域网:探索网络的规模与奥秘(3/10)

一、局域网的特点 局域网覆盖有限的地理范围&#xff0c;通常在几公里以内&#xff0c;具有实现资源共享、服务共享、维护简单、组网开销低等特点&#xff0c;主要传输介质为双绞线&#xff0c;并使用少量的光纤。 局域网一般是方圆几千米以内的区域网络&#xff0c;其特点丰富…

EMD-KPCA-Transformer多变量回归预测!分解+降维+预测!多重创新!直接写核心!

EMD-KPCA-Transformer多变量回归预测&#xff01;分解降维预测&#xff01;多重创新&#xff01;直接写核心&#xff01; 目录 EMD-KPCA-Transformer多变量回归预测&#xff01;分解降维预测&#xff01;多重创新&#xff01;直接写核心&#xff01;效果一览基本介绍程序设计参…

编程之路,从0开始:文件操作(2)

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 今天我们来继续学习C语言的文件操作。 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;编程之路 持续更新高质量内容&#xff0c;欢迎点赞、关注&…

mybatis学习(三)

声明&#xff1a;该内容来源于动力节点&#xff0c;本人在学习mybatis过程中参考该内容&#xff0c;并自己做了部分笔记&#xff0c;但个人觉得本人做的笔记不如动力节点做的好&#xff0c;故使用动力节点的笔记作为后续mybatis的复习。 六、在WEB中应用MyBatis&#xff08;使…

ES6 模块化语法

目录 ES6 模块化语法 分别暴露 统一暴露 ​编辑 默认暴露 ES6 模块化引入方式 ES6 模块化语法 模块功能主要由两个命令构成&#xff1a;export 和 import。 ⚫ export 命令用于规定模块的对外接口&#xff08;哪些数据需要暴露&#xff0c;就在数据前面加上关键字即可…

【Spring boot】微服务项目的搭建整合swagger的fastdfs和demo的编写

文章目录 1. 微服务项目搭建2. 整合 Swagger 信息3. 部署 fastdfsFastDFS安装环境安装开始图片测试FastDFS和nginx整合在Storage上安装nginxnginx安装不成功排查:4. springboot 整合 fastdfs 的demodemo编写1. 微服务项目搭建 版本总结: spring boot: 2.6.13springfox-boot…

无线电磁波在自由空间的衰减

自由空间损耗&#xff0c;指的是电磁波在空气中传播时候的能量损耗&#xff0c;电磁波在穿透任何介质的时候都会有损耗。在传输路径上的损耗&#xff0c;即为路径损耗。 自由空间路径损耗&#xff08;Free Space Path Loss&#xff09;的基本公式&#xff1a; 简化的自由空间损…

UE5实现可销毁对象的淡化销毁

进入对象材质 设置 的不透明蒙版 不透明蒙版见 UE材质不透明蒙版选项-CSDN博客 默认混合模式(不透明)下无法进行设置&#xff0c;将混合模式修改为 混合模式见 UE5材质混合模式-CSDN博客 新添加Texture sample节点 关于Texture sample&#xff1a;UE5材质Texture Sample …

【Linux学习】【Ubuntu入门】1-7 ubuntu下磁盘管理

1.准备一个U盘或者SD卡&#xff08;插上读卡器&#xff09;&#xff0c;将U盘插入主机电脑&#xff0c;右键点击属性&#xff0c;查看U盘的文件系统确保是FAT32格式 2.右键单击ubuntu右下角图标&#xff0c;将U盘与虚拟机连接 参考链接 3. Ubuntu磁盘文件&#xff1a;/dev/s…

文件的处理(c语言)

首先了解下文件的作用 文件可以把数据直接放在电脑的硬盘上&#xff0c;实现了数据的持久化 什么是文件 文件就是磁盘上的文件。在程序设计中&#xff0c;文件通常有俩种&#xff0c;一种是程序文件&#xff0c;另一种是数据文件&#xff08;这是从文件功能来分类的&#xff…

shell编程之awk

awk 是 Linux 以及 UNIX 环境中现有的功能最强大的数据处理工具。简单地讲&#xff0c; awk 是一种处理文本数据的编程语言。awk 的设计使得它非常适合于处理由行和列组成的文本数据。而在 Linux 或者 UNIX 环境中&#xff0c;这种类型的数据是非常普遍的。 除此之外&#xff…

数据库-基础理论

文章目录 前言一、ORM框架二、ACID原则三、事务Transaction四、N1问题五、Normalization三范式六、FMEA方法论&#xff08;Failure Mode and Effects Analysis&#xff09;七、Profiling和PerformanceSchema查询分析 前言 基础理论 ORM框架、ACID原则、事务Transaction、N1问…

用 Python 从零开始创建神经网络(九):反向传播(Backpropagation)

反向传播&#xff08;Backpropagation&#xff09; 引言1. 分类交叉熵损失导数&#xff08;Categorical Cross-Entropy loss derivative&#xff09;2. 分类交叉熵损失导数 - 代码实现3. Softmax激活函数导数&#xff08;Softmax activation derivative&#xff09;4. Softmax激…

Transformer详解及衍生模型GPT|T5|LLaMa

简介 Transformer 是一种革命性的神经网络架构&#xff0c;首次出现在2017年的论文《Attention Is All You Need》中&#xff0c;由Google的研究团队提出。与传统的RNN和LSTM模型不同&#xff0c;Transformer完全依赖于自注意力&#xff08;Self-Attention&#xff09;机制来捕…