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

在这里插入图片描述



总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

resnet50,clip,Faiss+Flask简易图文搜索服务

一、实现 文件夹目录结构&#xff1a; templates -----upload.html faiss_app.py 前端代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widt…

SFP+光模块介绍

SFP光模块介绍 1 SFP光模块简介(Small Form -Factor Pluggable)2 光模块管脚定义 1 SFP光模块简介(Small Form -Factor Pluggable) 光模块&#xff08;Optical Module&#xff09;由光电子器件、功能电路和光接口等组成&#xff0c;光电子器件包括激光发射器(Laser Transmitte…

Redis——Raft算法

Raft使用较为广泛的强一致性、去中心化、高可用的分布式协议&#xff0c;即使在网络、节点故障等情况下&#xff0c;多个节点依然能达到一致性。 其中redis、etcd等都用到了这种算法 在Redis集群中&#xff0c;采取的主从复制结构&#xff0c;当主节点宕机后&#xff0c;哨兵会…

【网络系统管理】2023年全国职业院校技能大赛:组策略--10套题组合--3

11、阻止Microsoft账户登录 (1)计算机配置\策略\Windows设置\安全设置\本地策略\安全选项 12、允许更改系统时间 (1)计算机配置\策略\Windows设置\安全设置\本地策略\用户权限分配 13、可以登录本机的用户 (1)计算机配置\策略\Windows设置\安全设置\本地策略\用户权限…

Glide源码学习

前言 开始 基本使用非常简单&#xff0c;链式调用把context对象传入&#xff0c;设置要加载的URL&#xff0c;设置要填充的ImageView控件&#xff0c;方法很简洁&#xff0c;每次都是传入的最核心的参数&#xff0c;底层加载的缓存逻辑&#xff0c;加载的引擎&#xff0c;加载…

基于RFSOC实现LFMCW雷达测距测速

雷达原理可以参考以下文章 https://zhuanlan.zhihu.com/p/508764579 一般情况下&#xff0c;雷达发射信号的模型可采用线性调频连续波&#xff08;LFMCW&#xff09; &#xff0c;发射波形的信号形式为调频连续锯齿波。线性调频的含义即调制信号频率随时间线性变化&#xff0c…

VELO SkyOW+坐垫,一起Cityride温暖你的上海之旅

随着冬季的到来&#xff0c;上海的街头巷尾弥漫着一种独特的浪漫气息&#xff0c;当金黄的落叶从空中飘落&#xff0c;铺满路边&#xff0c;只是路过就仿佛骑进了一幅世界名画。无论是沿着外滩漫游&#xff0c;还是穿行在浦东的高楼间&#xff0c;骑行的方式总能让你充分体验到…

基于lora的llama2二次预训练

基于lora的llama2二次预训练 一、为什么需要对llama2做基于lora的二次预训练? 加入中文训练语料进行llama2的二次预训练&#xff0c;这样模型就可以增加支持中文输出的能力。 二、基于lora的llama2二次预训练的目标是什么&#xff1f; 在保持预训练模型权重不变的情况下&a…

探索 Python 任务自动化的新境界:Invoke 库揭秘

文章目录 探索 Python 任务自动化的新境界&#xff1a;Invoke 库揭秘背景&#xff1a;为何选择 Invoke&#xff1f;什么是 Invoke&#xff1f;如何安装 Invoke&#xff1f;5个简单的库函数使用方法1. 定义任务2. 带参数的任务3. 运行 Shell 命令4. 任务参数化5. 列出任务 场景应…

利用Prompt工程为LLM提升推理能力

利用Prompt工程为LLM提升推理能力 基于策略的推理详解ReAct: 推理与行动思维链&#xff1a;逐步解决问题反思&#xff1a;深入分析和自我审查与代理架构的集成实际应用代码附录 众所周知&#xff0c;一个精心设计的Prompt能够显著增强大型语言模型&#xff08;LLMs&#xff09;…

C#开发合集

用C#轻松搞定m3u8视频下载与合并 嘿&#xff0c;程序员们&#xff01;今天咱们来聊聊如何用C#写个小程序&#xff0c;轻松下载和合并m3u8视频文件。没错&#xff0c;就是那种分段的流媒体视频。准备好了吗&#xff1f;让我们开始吧&#xff01; 准备工作 在动手之前&#xf…

java框架Netty网络编程——问鼎篇

Netty进阶 粘包现象 案例 服务端代码 public static void main(String[] args) {NioEventLoopGroup bossGroupnew NioEventLoopGroup(1);NioEventLoopGroup workerGroupnew NioEventLoopGroup(2);try {ServerBootstrap serverBootstrap new ServerBootstrap();serverBootstr…

堤防安全监测系统方案

一、背景情况 堤防是开发利用水资源和防治水灾害的重要工程措施之一&#xff0c;对防洪、供水、生态、发电、航运等至关重要。我国现有堤防9.8万多座&#xff0c;其中大中型堤防4700多座、小型堤防9.4万座&#xff0c;80%以上修建于上世纪50至70年代。由于堤防管护力量薄弱&am…

模型减肥秘籍:模型压缩技术 知识蒸馏

教程链接&#xff1a;模型减肥秘籍&#xff1a;模型压缩技术-课程详情 | Datawhale 知识蒸馏&#xff1a;让AI模型更轻更快 在人工智能快速发展的今天&#xff0c;我们经常需要在资源受限的设备&#xff08;如手机、IoT设备&#xff09;上运行AI模型。但这些设备的计算能力和…

golang实现TCP服务器与客户端的断线自动重连功能

1.服务端 2.客户端 生成服务端口程序: 生成客户端程序: 测试断线重连: 初始连接成功

React表单联动

Ant Design 1、dependencies Form.Item 可以通过 dependencies 属性&#xff0c;设置关联字段。当关联字段的值发生变化时&#xff0c;会触发校验与更新。 一种常见的场景&#xff1a;注册用户表单的“密码”与“确认密码”字段。“确认密码”校验依赖于“密码”字段&#x…

springboot实战(16)(Validation参数校验冲突问题、分组校验、默认分组)

目录 一、注解NotNull与NotEmpty区别。 二、Validation提供的分组校验。&#xff08;参数校验冲突问题&#xff09; &#xff08;1&#xff09;基本介绍。 &#xff08;2&#xff09;实际案例。 &#xff08;3&#xff09;大模型提问提供的方法。 1、定义分组接口。 2、在字段上…

学Linux的第九天--磁盘管理

目录 一、磁盘简介 &#xff08;一&#xff09;、认知磁盘 &#xff08;1&#xff09;结构 &#xff08;2&#xff09;物理设备的命名规则 &#xff08;二&#xff09;、磁盘分区方式 MBR分区 MBR分区类型 扩展 GPT格式 lsblk命令 使用fdisk管理分区 使用gdisk管理分…

【ubuntu+win】Win10+Ubuntu22.04双系统给ubuntu系统中的某个分区进行扩容(从400G->800G)数据无损坏

给ubuntu已分区的部分进行扩容 1. 准备扩容的空间2.进入ubuntu系统进行卸载分区3.安装图形界面的安装包4.进行对分区扩容5. 重新挂载 我的情况是这式的&#xff08;可以不看&#xff0c;直接看后面的&#xff09;&#xff1a; 刚开始买下电脑的时候&#xff0c;只装了一个 1T 的…

流式上传与分片上传的原理与实现

&#x1f680; 博主介绍&#xff1a;大家好&#xff0c;我是无休居士&#xff01;一枚任职于一线Top3互联网大厂的Java开发工程师&#xff01; &#x1f680; &#x1f31f; 在这里&#xff0c;你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人&#xff0c;我不仅热衷…