【C++笔记】C++之类与对象(下)
【C++笔记】C++之类与对象(下)
- 1、再看构造函数
- 1.1、构造函数的初始化列表
- 1.2、C++支持单参数的构造函数的隐式类型转换
- 1.3、匿名对象
- 2、Static成员
- 2.1、为什么要有静态成员变量?
- 2.2、一个类的静态成员变量属于这个类的所有对象
- 2.3、静态成员函数
- 3、友元
- 3.1、友元函数
- 3.2、友元类
- 4、内部类
- 4.1、内部类与外部类是互相独立的
- 4.1、内部类默认是外部类的友元
1、再看构造函数
1.1、构造函数的初始化列表
为什么要引入初始化列表呢?
我们之前知道,在编译器自动生成的构造函数中,对于自定义类型会去调用其默认构造函数,但要是这个自定义类型没有可用的默认构造函数,就会出问题了,例如:

这时候先要解决这个问题,就要用到初始化列表了,因为在初始化列表中就可以对这个b进行定义。
初始化列表的语法如下图所示:

也就是初始化列表是在构造函数的大括号之前,以冒号开始以逗号分隔每个成员的值用括号定义。
初始化列表是每个成员定义的地方:
初始化列表是每个成员定义的地方,所以有了初始化列表之后,在就如构造函数的函数体之前就会去走初始化列表,然后再去走函数体。
我们通过调试就可以看出:


事实上,不管你写不写每个成员都要走初始化列表,这个我们可以通过为成员加上缺省值来验证,因为成员的缺省值就是给初始化列表用的:

可以看到,虽然我们并没有在初始化列表里面写上_month的定义,但是当走到_day的定义的时候,_month就已经定义成了我们所给的缺省值了。这就说明了_month其实也走了初始化列表。
其实在走完_year时,编译器是会自动跳到成员列表处去定义_month的:

正是因为不管怎样,每个成员都会走初始化列表,所以我们以后可以用初始化列表就尽量要用初始化列表。
初始化列表初始化的顺序和声明的顺序相同:
这个可以通过一个“反常”现象进行验证:

对于这个程序我们一般都会理解成,输出两个1,但是结果却不是这样。这其实就恰恰说明了初始化列表初始化的顺序是和声明的顺序是一样的,因为是a2先声明,所以初始化列表会先走a2的定义,但a1的只还是随机值。
所以就出现了以上的结果,只要我们改一下它们在初始化列表中的顺序,这程序就正常了:

所以为了避免出现各种问题,我们一般都要保证初始化列表初始化的顺序和声明的顺序一样。
而在吧编译器自动生成的构造函数中,其实是在初始化列表中对内置类型不作处理(假如没有给缺省参数),对于自定义类型则去调用其默认构造函数:

1.2、C++支持单参数的构造函数的隐式类型转换
C++之所以支持这个语法,主要还是能个好的应付自定义类型的一些场景,还是拿我们的栈来举例子,对于下面这个类,我们在定义对象的时候其实有两种写法:

这里的本质其实就是隐式类型转换,编译器会先用2去调用A的构造函数去生成一个临时对象,再用这个对象去拷贝构造a2。
但是编译器觉得先构造在拷贝构造太麻烦了,于是编译器就再次进行了优化,将拷贝构造省去,用2直接构造a2。
从下面的结果中我们也可以看到编译器只调用了构造函数:

1.3、匿名对象
在C语言中我们见过匿名结构体,在C++中也有一个匿名对象,即我们在定义对象的时候可以不给名字:

匿名对象的生命周期只在一行,我们可以通过加上析构函数来验证这一点:

我们会发现程序在运行下一行指令的时候,就会先去调用析构函数。
其实C++支持这个语法还是为了代码简便,例如我们现在有一个函数的参数是一个自定义类型,如果不支持匿名对象我们每次都要先定义一个有名对象再去传:

但是有了匿名对象之后,我们就可以直接传一个匿名对象了:

2、Static成员
2.1、为什么要有静态成员变量?
有时候我们可能会有这样的需求:统计一个类总共定义了对少个对象。
我们很容易会想到定义一个全局变量,然后再在构造函数和拷贝构造中让这个全局变量自加1:

但这个做法的缺点就在于全局变量的作用域太大了,很容易就会被修改,只要被外人一修改,这统计的就不对了。
所以为了解决这个问题,C++引入了静态成员变量:

2.2、一个类的静态成员变量属于这个类的所有对象
首先要说明的是静态成员变量并不在类里面,这一点可以通过计算类的大小来验证:

可以看到,A的大小为4,也就是说只计算了成员_a的大小,并没有计算N的大小。
实际上静态成员变量是存在于静态区的。
静态成员变量不能给缺省值,静态成员变量需要在类外边定义:

因为静态成员变量属于所有类,所以如果它是共有的,他就可以直接使用类作用限定符来访问,而其他成员变量就不可以:

2.3、静态成员函数
熟了静态成员变量,C++还有一个静态成员函数
静态成员函数没有this指针,所以静态成员函数不能访问非静态成员:

但是它可以自由的访问静态成员变量:

而且如上图所示,静态成员变量并没有this指针,所以在调用的时候也就不需要先有对象,直接是用来访问限定符突破类域即可。
3、友元
虽然在类里边我们可以随便访问成员变量而不受访问限定符的限制,但有些函数我们会发现将它写成成员函数会很奇怪,例如对日期我们需要使用运算符重载重载一个流插入运算符:

我们这好像写的没问题,但当我们去调用的时候却会发现问题了:

这里提示说未接收到参数,这是因为我们的顺序反了,我们知道非静态成员函数都会有一个隐藏的this指针,并且永远在第一位:

所以我们如果要调用,就需要这样:

这样就简直太奇怪了,而且使用起来也是真不习惯。
所以为了解决这个问题,我们就需要引入友元了。
3.1、友元函数
上面的这个问题主要是参数的顺序不对,所以我们可以考虑将其写成全局的,这样就可以随意安排参数的顺序了:

但是当我们在调用的时候却还是会出现问题:

因为这些成员都是私有的,我们不能够直接访问。
其实我们可以用一个简单的方法来解决,就是对应每一个成员都写一个共有的函数来返回对应成员的值:

但当成员有很多个的时候这种方法也不是很简便,所以我们就可以用到友元声明:

因为这里的友元函数仅仅只是个声明,所以写在任何地方都是可以的。
这样这个函数就可以直接访问这些成员了:

3.2、友元类
有时候我们需要在一个类里边定义另一个类的对象:

但是烦心的是我们并不能直接访问其成员,因为是私有的:

这时候我们就可以将Date类声明成A类的“友元类”,没错,友元不仅可以声明友元函数还可以声明友元类:

这样在Date类中就可以随意的访问A类中的成员了:

这其实和友元函数的作用是相同的。
但有一点需要注意:“友元”并不是相互的,就像这里只有Date类是A的友元类,但A并不是Date类的友元类,也就是说在A类中不可以直接访问Date类中的成员:

4、内部类
这就像结构体可以嵌套定义一样,类也可以嵌套定义:

4.1、内部类与外部类是互相独立的
“内部类”虽然名称叫做内部类,但事实上它本身却并不包含在外部类里,这一点我们可以通过计算类的大小来验证:

很明显这里A类的大小仅为4,如果要包含内部类的话至少也的是8才对。
但内部类受外部类的访问限定符的限制,比如内部类若是公有的我们就可以直接通过A的类作用限定符来定义对象:

如若是私有,就不能了:

4.1、内部类默认是外部类的友元
内部类的优势就是内部类默认是外部类的友元类,也就是说内部类可以直接访问到外部类的成员:

但上面也说过了,友元并不是相互的,所以外部类并不能直接访问内部类的成员:

相关文章:
【C++笔记】C++之类与对象(下)
【C笔记】C之类与对象(下) 1、再看构造函数1.1、构造函数的初始化列表1.2、C支持单参数的构造函数的隐式类型转换1.3、匿名对象 2、Static成员2.1、为什么要有静态成员变量?2.2、一个类的静态成员变量属于这个类的所有对象2.3、静态成员函数 3、友元3.1、…...
管理类联考——英语——实战篇——大作文——图表——动态图表——整体效果
动态图表模板 What is clearly presented in the above 图表类型 is that dramatic changes have taken place in 主题词1 from 年份1 to 年份2.During the period, there was a marked jump from 数字1 to 数字2 in 事物1,while that of 事物2 declined significantly from 数…...
threejs纹理加载三(视频加载)
threejs中除了能把图片作为纹理进行几何体贴图以外,还可以把视频作为纹理进行贴图设置。纹理的类型有很多,我们可以用不同的加载器来加载,而对于视频作为纹理,我们需要用到今天的主角:VideoTexture。我们先看效果&…...
VUE笔记(三)vue的语法
一、计算属性 1、计算属性的概念 计算属性是依赖于源数据(data或者属性中的数据),在元数据的基础上进行逻辑运算后得到的新的数据,计算属性要依赖于源数据,源数据数据变化计算属性也会变化 2、计算属性的语法 在vue2中使用computed这个选…...
探讨uniapp的路由与页面生命周期问题
1 首先我们引入页面路由 2 页面生命周期函数 onLoad() {console.log(页面加载)},onShow() {console.log(页面显示)},onReady(){console.log(页面初次显示)},onHide() {console.log(页面隐藏)},onUnload() {console.log(页面卸载)},onBackPress(){console.log(页面返回)}3 页面…...
咸鱼之王俱乐部网站开发
我的俱乐部 最新兑换码 *注意区分大小写,中间不能有空格! APP666 HAPPY666 QQ888 QQXY888 vip666 VIP666 XY888 app666 bdvip666 douyin666 douyin777 douyin888 happy666 huhushengwei888 taptap666 周活动 宝箱周 宝箱说明 1.木质宝箱开启1个…...
Electron+Vue3+TS 打包exe客户端
Electron Vue3 TS 实战 - 掘金 如果报错loaderContext.getOptions is not a function ts-loader版本不一致导致的问题。 解决方案:npm install ts-loader8.0.0 --save...
vue3范围选择组件封装
个人项目地址: SubTopH前端开发个人站 (自己开发的前端功能和UI组件,一些有趣的小功能,感兴趣的伙伴可以访问,欢迎提出更好的想法,私信沟通,网站属于静态页面) SubTopH前端开发个人站…...
能被整除的数(容斥原理)
思路: (1)需求:求对于1~n中至少能被p1~pm至少1个整除的数的个数,由于都是质数,彼此互质,不需要进行质因子分解,根据容斥原理, res n/p1 n/p2 ... n/pm - n /(p1p2) -…...
Modbus转Profinet网关与流量变送器兼容转ModbusTCP协议博图配置
首先,我们需要明确电磁流量计的通信协议是Modbus,而西门子1200PLC的通信协议是Profinet。这两种协议在功能和特性上存在一定的差异,因此需要使用兴达易控Modbus转Profinet网关设备进行转换。兴达易控的XD-MDPN100是Profinet转ModbusTCP的网关…...
HLS实现CORDIC算法计算正余弦并上板验证
硬件:ZYNQ7010 软件:MATLAB 2019b、Vivado 2017.4、HLS 2017.4、System Generator 2017.4 1、CORDIC算法计算正余弦 CORDIC算法详细分析网上有很多资料,它的原理是用一系列旋转去逼近目标角度,这一系列旋转的角度为 θ a r c t…...
高阶数据结构并查集
目录: 并查集的概念代码实现 LeetCode例题 并查集的概念 将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中反复遇到查询某一个元素属于那个集合的运算…...
WSL2连接不了外网怎么办?
某天忽然WLAN变成地球图标,上不了Internet,搞了半天网络适配器,仍然不行。回忆之前做过的操作,曾经运行过ZoogVPN,试着启动并连接,然后退出,WLAN神奇地恢复了连接,可以上Internet了。…...
【C/C++】探索内存对齐的奥秘与优势
目录 一,前言 二,什么是内存对齐? 三,内存对齐的原理 四,内存对齐的优势 五,如何实现内存对齐?(看这节就行) 1.使用 #pragma pack 来实现内存对齐的示例 七&#…...
leetcode分类刷题:滑动窗口(二、重复元素类型)
1、连续子数组、连续子串问题通常需要滑动窗口来求解,本篇文章对应的“二、重复元素类型”在此基础上对连续子数组、连续子串中重复元素个数、种类进行考察,此时,需要使用和维护哈希表进行左右指针的移动,因此这类题目对应的解法为…...
MySQL—buffer pool
一、buffer pool的介绍 Buffer pool是什么 一个内存区域,为了提⾼数据库的性能,数据库操作数据的时候,把硬盘上的数据加载到buffer pool,不直接和硬盘打交道,操作的是 buffer pool的数据,数据库的增删改查…...
《C和指针》笔记8: 枚举类型
枚举 (enumerated)类型就是指它的值为符号常量而不是字面值的类型,它们以下面这种形式声明: enum Jar_Type { CUP, PINT, QUART, HALF_GALLON, GALLON };这条语句声明了一个类型,称为Jar_Type。这种类型的变量按下列方式声明: e…...
Python爬虫框架之Selenium库入门:用Python实现网页自动化测试详解
概要 是否还在为网页测试而烦恼?是否还在为重复的点击、等待而劳累?试试强大的Selenium!让你的网页自动化测试变得轻松有趣! 一、Selenium库到底是什么? Selenium 是一个强大的自动化测试工具,它可以让你直…...
docker swarm 部署服务网络问题
docker swarm 服务部署问题 docker swarm 部署服务时可能会出现,启动服务特别慢的情况,甚至一个service 启动后,容器会长时间处于 preparing 状态,直到 状态切换成 running 状态后,才会启动下一个service。然后查询资…...
1.00001git源码clone后进行编译(带调试)
– 新建用户 useradd postgres passwd postgres – 用户加入sude组 先cd到/etc/sudoers目录下 由于sudoers文件为只读权限,所以需要添加写入权限,chmod uw sudoers vim sudoers 找到root ALL (ALL) ALL这一行,在下一行加入username ALL (A…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...
GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...
HTML中各种标签的作用
一、HTML文件主要标签结构及说明 1. <!DOCTYPE html> 作用:声明文档类型,告知浏览器这是 HTML5 文档。 必须:是。 2. <html lang“zh”>. </html> 作用:包裹整个网页内容,lang"z…...
