《类和对象(下)》

引言:

书接上回,如果说类和对象)是入门阶段,类和对象)是中间阶段,那么这次的类和对象)就可以当做类和对象的补充及收尾。

一:再探构造函数

  1. 之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方式,就是初始化列表初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
  2. 每个成员变量在初始化列表中只能出现一次语法理解上初始化列表可以认为是每个成员变量定义初始化的地方
  3. 引用成员变量const成员变量没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。
  4. C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。
  5. 尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。
  6. 初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持一致

场景一:初始化列表形式

在这里插入图片描述
在这里插入图片描述
解读:这里我们虽然在初始化列表中没有对_day初始化,但是在声明中给了缺省值,所以这时在初始化列表中就会拿其缺省值来进行初始化。
这里需要注意的是声明这里只是给缺省值,并不是初始化。

场景二:自定义类型的初始化列表

在C++中规定,如果自定义类型的成员没有默认构造函数,这时候自定义类型就需要自己来写构造函数,而且如果成员还是类类型的变量就必须用初始化列表来初始化
这里我们拿之前的stackMyqueue 来举例子:

在这里插入图片描述
这里Stack构造函数我们故意显示的写成带参的,这样编译器就不会再生成构造函数,这时Stack就是没有默认构造函数的。
在这里插入图片描述

这里可以看到这里在创建Myqueue类型的对象时就无法初始化,那么这时候就需要自己来写构造函数,但是这里怎么初始化呢?没法写啊,这里就需要用到初始化列表
在这里插入图片描述

这样就可以实现自定义类型中类类型成员的初始化了。

牛刀小试:

下面这个程序的运行结果?
在这里插入图片描述

解析:由于初始化列表在初始化时是按照成员的声明顺序进行初始化的,所以这里是先初始化_a2 ,但是这时_a1还是一个随机值,因此_a2被初始化为了随机值,接着再拿1来初始化_a1,所以_a1为1,_a2为随机值。

下面我们运行程序来验证一下:
在这里插入图片描述
和我们分析的一样。

小结:成员变量走初始化列表的逻辑

  1. 无论是否显示写初始化列表,每个构造函数都有初始化列表。
  2. 无论是否在初始化列表显示初始化成员变量,每个成员变量都要走初始化列表初始化。

在这里插入图片描述

二:类型转换

  1. C++支持内置类型隐式类型转换为类类型对象,但是需要有相关内置类型为参数的构造函数。
  2. 构造函数前面加explicit就不再支持隐式类型转换
  3. 类类型的对象之间也可以隐式转换,需要相应的构造函数支持。

场景一:内置类型转类类型对象

在这里插入图片描述
这是我们写的一个A

在这里插入图片描述
这里就是类型转换的一个场景,并且可以看到编译器对其进行了优化,省去了拷贝构造这一步骤。

场景二:对类型转换临时对象的引用

在这里插入图片描述

场景三:多参数转换

在这里插入图片描述

在这里插入图片描述
注:C++11之后才支持的多参数的类型转换。

场景四:类类型对象的隐式转换

在这里插入图片描述
在这里插入图片描述

场景五:explicit 来禁用隐式类型转换

在这里插入图片描述
在这里插入图片描述
这里我们对单参数的那个构造函数加了explicit来修饰,所以单参数的隐式类型转换就不支持了,但是两个参数的隐式类型转换还是支持的。

小结:

  1. 如果想用隐式类型转换,需要有对应的内置类型为参数的构造函数来支持。
  2. 对于这个过程是否优化,取决于具体的编译器。

三:static成员

  1. static修饰的成员变量,称之为静态成员变量静态成员变量一定要在类外进行初始化
  2. 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。
  3. static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。
  4. 静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。
  5. 非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
  6. 突破类域就可以访问静态成员,可以通过类名::静态成员 或者 对象.静态成员 来访问静态成员变量静态成员函数
  7. 静态成员也是类的成员,受public、protected、private访问限定符的限制。
  8. 静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。

场景一:统计编译器创建了多少类

在这里插入图片描述
在这里插入图片描述
可以看到是创建了三个

场景二:解决特殊问题

1. 题目:

在这里插入图片描述

2.分析:

这道题要求我们计算1到n的和,看似简单,但这道题给了我们一堆限制条件,这一下子让我们没有了思路,这里我就直接说怎么做了,我们可以利用构造函数自动调用的特性来解决,创建一个静态变量,然后每次自动调用构造函数时都累加上这个变量上,最后就能计算出1到n的和

3. 代码:

在这里插入图片描述

4. 题目传送门:

JZ64 求1+2+3+…+n

小结:

静态变量的意义:尽可能减少全局变量的使用。

四:友元

  1. 友元提供了一种突破类访问限定符封装的方式,友元分为:友元函数友元类,在函数声明或者类声明的前面加friend,并且把友元声明放到一个类的里面。
  2. 外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。
  3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  4. 一个函数可以是多个类的友元函数。
  5. 友元类中的成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有和保护成员。
  6. 友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。
  7. 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。
  8. 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用

流插入与流提取

在这里插入图片描述
我们知道C++中的输入输出可以自动识别类型,所谓的自动识别其实还是函数重载的作用。
在这里插入图片描述

在C++网站上查询ostream时可以看到它的一堆重载。
分析打印过程:
在这里插入图片描述

1. 流插入重载

这里我们就拿之前实现的日期类来实现一下自定义类型流插入流提取的重载

一开始我们可能会这样写:
在这里插入图片描述
在这里插入图片描述
但是打印的时候我们这样写还不行,因为参数匹配反了,在类里面实现的重载,第一个参数默认为this指针,在这里this指针接收的是d1的地址,所以第一个参数是类类型的指针,第二个参数才是ostream,因此打印的时候要这样写:
在这里插入图片描述
但是这样写跟我们之前的打印不一样,感觉挺尴尬的。
为什么库函数里面实现的重载参数就能匹配上呢?
在这里插入图片描述

那是因为库函数里面是在ostream类里面实现的,this指针接收的是ostream类类型,我们在调用函数时,正好将cout传过去被this指针接收,因此就对上了,但是我们不能随意修改库函数啊,所以我们就只好将其写成全局的函数了。

在这里插入图片描述

这样的话参数匹配不上的问题就解决了,但是这里又遇到了新的问题:这里牵扯到了访问私有成员,如果像之前一样都写单独的成员函数来获取的话就特别麻烦,因此这里我们引入 友元 这一概念来解决这一问题。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注:这里为了避免链接错误,可以让这个重载函数成为内联函数。

2. 流提取重载

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注:这里需要注意的是第二个参数就不能加const了,因为如果加const的话就该参数就为常量,就不能再给其输入值了。

加上这两个其实这个日期类就比较完善了,但是我们在输入时还需要考虑一种情况:如果输入的日期非法呢?所以在输入这里我们可以再完善一下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
经过上面的补充,这个日期类就相对完善了。

同一个函数的多个友元

在这里插入图片描述
在这里插入图片描述

小结:

虽然友元有时候提供了便利。但是友元在一定程度上会增加耦合度,破坏了封装,所以友元不宜多用

五:说明

前四个板块是需要重点来理解和掌握的,后面几个简单了解

六:内部类

  1. 如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在全局相比,内部类只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类
  2. 内部类默认是外部类的友元类
  3. 内部类本质也是一种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。

场景一:计算带有内部类的类大小

在这里插入图片描述

可以看到计算外部类A的时候,内部类并没有计入其中。

场景二:友元性质

在这里插入图片描述
在这里插入图片描述
注:由于内部类B默认为外部类A的友元类,因此B可以访问A的私有成员_a

七:匿名对象

  1. 类型(实参)定义出来的对象叫做匿名对象,相比之前我们定义的类型 对象名 (实参)定义出来的叫有名对象
  2. 匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象。

场景一:有名对象 临时对象 匿名对象 举例

在这里插入图片描述

场景二:用匿名对象来简化步骤(特定情况下)

在这里插入图片描述

解读:当我们只是单纯地想调用某个函数时,先创建有名对象再通过对象调用这个函数就比较繁琐,通过匿名对象我们就可以一步完成调用,而且匿名对象的生命周期只是当前一行,就像一次性纸杯一样。
注:const可以延长临时变量的生命周期

八:对象拷贝时的编译器优化

  1. 现代编译器会为了尽可能提高程序的效率,在不影响正确性的情况下会尽可能减少一些传参和传返回值的过程中可以省略的拷贝
  2. 如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理。当前主流的相对新⼀点的编译器对于连续⼀个表达式步骤中的连续拷贝会进行合并优化,有些更新更"激进"的编译器还会进行跨行跨表达式的合并优化。
  3. linux下可以将下面代码拷贝到test.cpp⽂件,编译时用 g++ test.cpp -fno-elide- constructors 的方式关闭构造相关的优化。

场景一:隐式类型转换时的编译器优化

在这里插入图片描述

  1. 正常步骤:1构造临时对象,临时对象再拷贝构造aa1
  2. 优化之后:1直接构造aa1

场景二:传值传参(无优化)

在这里插入图片描述

  1. 在创建对象aa2时调用了构造函数,在传值传参的时候调用了拷贝构造函数。
  2. 在传值传参时,编译器没有进行优化。

场景三:隐式类型传参

在这里插入图片描述

  1. 正常步骤:1构造临时对象,这个临时对象再拷贝构造给一个新的对象。
  2. 优化后:1直接构造新的对象。

场景五: 传值返回

在这里插入图片描述

  1. 正常步骤:先构造局部对象aa,再拷贝构造一份aa的形参(临时对象)返回。
  2. 优化后:直接构造然后返回

场景六: 拷贝构造+赋值重载(无法完全优化)

在这里插入图片描述

  1. 正常步骤:aa先构造局部对象,然后调用拷贝构造函数来生成一份要返回的临时对象,然后接收返回值的对象aa1来接收这个临时对象,这里会调用拷贝构造函数,之后aa2调用构造函数初始化,aa2 再调用赋值重载函数来拿aa1来完成赋值。
  2. 优化后: 编译器进行了跨行合并优化,将构造的局部对象aa和拷贝临时对象合
    并为一个直接构造,剩下的没有进行优化。

小结:

  1. 不同版本的编译器以及不同的编译器的优化是不确定的。
  2. 在接收返回值是用拷贝构造比赋值重载的方式更好,更方便编译器的优化。

总结:

到这里,类和对象我们就算是学完了,可以说已经入门C++了(开心),不过后面要学的东西还多呢(大声)
本篇完结撒花!!!

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

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

相关文章

Java MVC

在软件开发中,MVC(Model-View-Controller)是一种常用的设计模式,它将应用程序分为三个核心部分:模型(Model)、视图(View)和控制器(Controller)。这…

嵌入式学习笔记 - 关于单片机的位数

通常我们经常说一个单片机是8位的,16位的,32位的,那么怎么判断一款单片机的位数是多少位呢,判断的依据是什么呢, 一 单片机的位数 单片机的位数是指单片机数据总线的宽度,也就是一次能处理的数据的位数&a…

推荐几个常用免费的文本转语音工具

推荐几个常用免费的文本转语音工具 在数字内容创作的时代,文本转语音(TTS)技术已经成为内容创作者的得力助手。无论是制作视频配音、有声读物、还是为网站增加语音功能,这些工具都能大幅提高创作效率。今天,我将为大家推荐几款优质的免费文本…

Microsoft Azure DevOps针对Angular项目创建build版本的yaml

Azure DevOps针对Angular项目创建build版本的yaml,并通过变量控制相应job的执行与否。 注意事项:代码前面的空格是通过Tab控制的而不是通过Space控制的。 yaml文件中包含一下内容: 1. 自动触发build 通过指定code branch使提交到此代码库的…

Python Day23 学习

继续SHAP图绘制的学习 1. SHAP特征重要性条形图 特征重要性条形图(Feature Importance Bar Plot)是 SHAP 提供的一种全局解释工具,用于展示模型中各个特征对预测结果的重要性。以下是详细解释: 图的含义 - 横轴:表示…

.NET 8 + Angular WebSocket 高并发性能优化

.NET 8 Angular WebSocket 高并发性能优化。 .NET 8 WebSocket 高并发性能优化 WebSocket 是一种全双工通信协议,允许客户端和服务端之间保持持久连接。在高并发场景下,优化 WebSocket 的性能至关重要。以下是针对 .NET 8 中 WebSocket 高并发性能优化…

Ubuntu 22.04.5 LTS 基于 kubesphere 安装 cube studio

Ubuntu 22.04.5 LTS 基于 kubesphere 安装 cube studio 前置条件 已经成功安装 kubesphere v4.3.1 参考教程: https://github.com/data-infra/cube-studio/wiki/%E5%9C%A8-kubesphere-%E4%B8%8A%E6%90%AD%E5%BB%BA-cube-studio 1. 安装基础依赖 # ubuntu安装基础依赖 apt insta…

centos 7 安装 java 运行环境

centos 7 安装 java 运行环境 java -version java version "1.8.0_131" Java(TM) SE Runtime Environment (build 1.8.0_131-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)java -version java version "1.8.0_144" Java(TM) …

Linux系统管理与编程20:Apache

兰生幽谷,不为莫服而不芳; 君子行义,不为莫知而止休。 做好网络和yum配置,用前面dns规划的www的IP进行。 #!/bin/bash #----------------------------------------------------------- # File Name: myWeb.sh # Version: 1.0 # …

.NET 在鸿蒙系统上的适配现状

目录 .NET 在鸿蒙系统上的适配现状 鸿蒙系统对虚拟机的限制与.NET的适配挑战 NativeAOT 在鸿蒙系统中的适配原理与实现方式 已知问题与解决方案:鸿蒙系统中的 syscall 限制 鸿蒙系统适配中的技术难点与解决方案 跨平台编译的挑战与应对策略 依赖库管理与兼容…

kotlin JvmName注解的作用和用途

1. JvmName 注解的作用 JvmName 是 Kotlin 提供的一个注解,用于在编译为 Java 字节码时自定义生成的类名或方法名。 作用对象: 文件级别(整个 .kt 文件)函数、属性、类等成员 主要用途: 控制 Kotlin 编译后生成的 JV…

树莓派4 yolo 11l.pt性能优化后的版本

树莓派4 使用 Picamera2 拍摄图像,然后通过 YOLO11l.pt 进行目标检测,并在实时视频流中显示结果。但当前的代码在运行时可能会比较卡顿,主要原因包括: picam2.capture_array() 是一个较慢的操作;YOLO 推理可能耗时较长…

Docker私有仓库实战:官方registry镜像实战应用

抱歉抱歉,离职后反而更忙了,拖了好久,从4月拖到现在,在学习企业级方案Harbor之前,我们先学习下官方方案registry,话不多说,详情见下文。 注意:下文省略了基本认证 TLS加密&#xff…

MySQL 安全架构:从渗透测试到合规审计

MySQL 安全架构:从渗透测试到合规审计 一、数据库安全的时代挑战与核心需求 在数据成为企业核心资产的今天,MySQL 面临的安全威胁日益复杂。据统计,2024 年全球数据库泄露事件中,关系型数据库占比高达 68%,其中 MySQ…

【基础复习笔记】计算机视觉

目录 一、计算机视觉基础 1. 卷积神经网络原理 2. 目标检测系列 二、算法与模型实现 1. 在PyTorch/TensorFlow中实现自定义损失函数或网络层的步骤是什么? 2. 如何设计一个轻量级模型用于移动端的人脸识别? 3. 描述使用过的一种注意力机制&#…

Django 项目的 models 目录中,__init__.py 文件的作用

在 Django 项目的models/init.py文件中,这些导入语句的主要作用是将各个模型类从不同的模块中导入到models包的命名空间中。这样做有以下几个目的: 简化导入路径 当你需要在项目的其他地方使用这些模型时,可以直接从models包导入&#xff0c…

实现一个简单的 TCP 客户端/服务器

注意: TCP 三次握手建立连接建立连接后,TCP 提供全双工的通信服务,也就是在同一个连接中,通信双方 可以在同一时刻同时写数据,相对的概念叫做半双工,同一个连接的同一时刻,只能由一方来写数据T…

专业课复习笔记 9

前言 学爽了。 为什么哈希函数的空间复杂度是 O(N) 我们实际使用的电话号码的数目是 N &#xff0c;理论上至多有 R 个电话号码&#xff0c;桶数组 bucket array 的容量是 M &#xff0c;满足条件 N < M < < R N<M<<R N<M<<R&#xff0c;因为动…

【论文阅读27】-TCN–BiLSTM -滑坡预测

《A Landslide Displacement Prediction Model Based on the ICEEMDAN Method and the TCN–BiLSTM Combined Neural Network》 发表于 Water 期刊&#xff0c;2023年。 &#x1f4cc; 主要内容概述 这篇论文提出了一种滑坡位移预测模型&#xff0c;结合了&#xff1a; ICEEM…

8b10b编解码仿真

一、基本概念 8B/10B编码&#xff08;8-bit to 10-bit encoding&#xff09;是一种将8位数据&#xff08;包括数据字符和控制字符&#xff09;转换为10位符号&#xff08;Symbol&#xff09;的编码技术&#xff0c;由IBM工程师Al Widmer和Peter Franaszek于1983年提出。其核心思…