C++语言·类和对象(下)

1. 初始化列表

        我们回忆上节写的MyQueue类,其中有两个栈类和一个int类型,栈类因为其特殊性,要开空间,所以我们必须手搓Stack类的构造函数。但是正常来说MyQueue自动生成的构造函数会调用自定义类型的默认构造函数,也就是说我们不必再手搓MyQueue类的构造函数了,编译器自动生成的就好用。

        但是此时问题来了,默认构造函数简单点理解就是不用传参就能调用的构造函数,那么如果我们手搓的Stack类没有默认构造函数,那么MyQueue就不能自动生成构造函数了,因为它没得调了,就像这样。

                

        光标圈出来的那一行中,我们没有给构造函数搞缺省参数,所以它必须传参才能调用,也就是说此时Stack类没有了默认构造函数,那么现在编译器是处在报错的状态,那我们如何通过写MyQueue的构造函数来解决这一问题。

        此时就要说到初始化列表的概念了,初始化列表本质上可以理解成每个对象中成员定义的地方,所有成员你可以在初始化列表初始化,也可以在函数体内部初始化。

        具体写法就是:以一个冒号开始,接着是一个以逗号分开的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。

                        

        ps: 这里面size其实应该前面有个下划线的,但是我忘记写了,大家凑活着看吧

        _pushst 和 _popst 的初始化必须要在函数体外面写,因为他俩进了函数体就没法初始化了呀,不过size初始化可以写到函数体里头,当写在函数体里头的时候有必须要用赋值的方案进行初始化了。

        虽说类中的所有成员都可以在初始化列表和函数体内初始化,但是有三类成员必须在初始化列表中初始化:1. 引用        2. const成员        3. 没有默认构造的自定义类型成员

        我们知道如果在定义成员变量的时候赋值,我们管这个行为叫给成员变量缺省值,这个缺省值的含义就是为了给初始化列表的缺省值。因为初始化列表不管你有没有显式写,编译器都会自动给每个成员都走一遍,自定义类型的成员会调用其默认构造函数,内置类型就不做操作(除非有缺省值,那么编译器的行为相当于自动在括号中写那个缺省值)。这就解释了上面Stack没有默认构造函数MyQueue就不能自动生成构造函数。

        既然是缺省值,我们在显式写初始化列表的时候就可以更改,同时默认构造函数那个赋的不也是缺省值嘛,因此也可以更改。

                ​​​​​​​        

        这里强调一下,缺省值是可以给表达式的,比如我们可以给一个指针成员一个malloc缺省值。

        那么函数体中的语句的优先顺序比初始化列表还要高,也就是说初始化列表中已经初始化好的成员我们还可以在函数体中进一步修改。具体优先顺序就是: 缺省值 < 初始化列表 < 函数体

        成员变量在类中声明次序就是其在初始化列表中的初始化顺序,初始化顺序与其在初始化列表中的先后次序无关。我们看下面这段代码的输出就懂了

        ​​​​​​​        

        因为是先初始化的_a2所以输出的不是两个1,而是1和随机值。

        最后在实践中,尽可能使用初始化列表进行初始化,不方便再用函数体初始化。

2. 对象赋值时的隐式类型转换

        我们看下面这一段代码

                        ​​​​​​​​​​​​​​

        我们知道aa1通过构造函数赋值,aa2通过拷贝构造赋值,那aa3是怎么回事?raa又是怎么回事?

        aa3这里其实产生了隐式类型转换,由内置类型转换为了自定义类型。首先3先构造一个A类型的临时对象,再用这个临时对象拷贝构造aa3。所以说单参数构造函数可以支持这种直接赋内置类型的操作,它会产生隐式类型转换。现在我们在看打印出来的结果,当 3 赋给 aa3 的时候只调用了构造函数,这是因为编译器遇到 连续构造+拷贝构造 的组合时会优化成直接构造.

        raa的本意是取引用,首先 3 先隐式转换出来一个临时对象,为了权限不放大,所以要给const,之后raa引用3转换出来的这个新的临时对象。所以只有 3 隐式转换的时候出现了构造函数,没有拷贝构造出现。

        这个隐式类型转换的意义就是在于能够简化我们的代码量,有时还能缩减运行效率,直接赋一个内置变量,让编译器自己去类型转换。比如说我们想往栈里头压这个A类对象:

        ​​​​​​​        ​​​​​​​        

        我们可以选择先构造一个对象,然后再给到压栈。或者直接不写构造了,直接压1,让编译器自己类型转换去,这不就方便多了。

        上面是单参数的隐式类型转换,那多参数的写法是这样的:

        ​​​​​​​        ​​​​​​​        

        给值的时候用花括号括起来,写不写等于号都行,但是推荐写上等于号,这样和普通括号有点区别。

2.1 explicit关键字

        如果不想让构造能发生隐式类型转换,就在构造函数前面加上explicit关键字就行,单参数和多参数通用的。

        ​​​​​​​        ​​​​​​​        ​​​​​​​​​​​​​​

        aaa1因为隐式类型转换被拒所以无法构造了,那个aaa2没报错是因为编译器在这里把花括号当括号处理了。我们看aaa掉成员函数时的情况,传对象的时候就通过了,但是尝试传内置类型的时候隐式类型转换被拒绝了,因此编译报错。

3. static 静态成员

        声明为static的类的成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员函数一定要在类外进行初始化。

        静态成员有如下特性:

        1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。

        2. 静态成员变量必须定义在类外定义,定义时不添加static关键字,类中只是声明

        3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员访问

        4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员

        5. 静态成员也是类的成员,受 public,protected,private 访问限定符的限制

        ​​​​​​​        ​​​​​​​        

        我们看上面一段代码。

        _scount是一个静态成员变量,这里我将它开放成了public,否则无法在类外访问。它存储于静态区,并属于所有的A类和其实例(对象),也就是说它可以直接通过类 A::_scount 访问,同时从任何地方访问(通过类访问,或通过对象访问)并改变其值的操作,都会直接改变其在静态区存储的值,也就是说任何依次改变都会影响A类以及其下所有实例中的_scount值,这就是所谓的静态成员变量属于所有类对象。同时因为它存储于静态区,所以在实例化类的时候静态成员变量同成员函数一样,不占用对象的空间。

        _scount在类中声明的时候不能给缺省值,给了就报错,因为给缺省值是为了在初始化列表中初始化,但是_scount不走初始化列表,它是静态变量,因此必须在类外定义

        那么这个静态成员变量可以用来干嘛呢,举个例子,它可以实时检测该类存在几个实例化对象,因为创建对象无非两种方法,构造和拷贝构造,因此我显式写每执行依次这两种构造就++_scount ,对象销毁析构的时候再 --_scount。最终打印出来的效果还是挺明显的。

        我们再看下一段代码,讲解一下静态成员函数

        ​​​​​​​        ​​​​​​​        ​​​​​​​

        这里我将_scount设置为private状态了,那么此时就不能再类外访问它了,也就是说无法这样 A::_scount 访问了,当然这也比较符合我们写成员变量的习惯,就是把所有成员变量都设置成private状态加以保护。那此时我们就要通过静态成员函数访问。static修饰变量和修饰函数的意义完全不一样,修饰变量时改变了变量的声明周期,修饰函数时改变了它的链接属性。那么静态成员函数的特性就是没有this指针,也就是说静态成员函数只能访问静态成员变量。

        那么使用的时候跟静态成员函数很像,既可以通过类访问,也可以通过对象访问得到_scount的值

4. 友元

        友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

        友元分为友元函数和友元类:

4.1 友元函数

        在上节重载流插入和流提取的时候,我们为了保证传参顺序符合习惯,因此将流插入和流提取的重载函数写在了类外,从而引发了无法访问private成员变量,当时我们给出的解决方案是写Get函数,取消private状态肯定是不可取的,要不我们还封装个什么。

        那么本节我们可以通过友元函数的设置来打破针对某一个函数的封装,其实上节最后实现日期类的时候我就已经搞友元了不知道大家注意到了没有。

                

        这里我把日期类简化了一下,就保留了流插入和流提取的内容,在类刚开始那两行用friend修饰的代码就是友元函数的修饰,用一个friend关键字修饰一个外部函数的声明,就能使这个外部函数访问类内部的private信息了。

4.2 友元类

        跟友元函数类似,在一个类中声明另一个类是友元的,那么就可以在另一个类中访问这个类的私有信息。形象一点来说就是:你是我的朋友,那么你就可以知道我的一些私有信息。

        ​​​​​​​        ​​​​​​​        

        B中可以访问A中的私有成员,但是A不能访问B的成员。

        最后还要说一下友元不能传递,就比如C是B的友元,B是A的友元,但是C不是A的友元。也就是说,A不能访问C的私有成员。

5. 内部类

        如果一个类定义在另一个类的内部,这个内部的类就叫做另一个类的内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的私有成员。外部类对内部类没有任何优越的访问权限。

        内部类天生是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员,但是外部类不是内部类的友元

        说白了就是外部类和内部类就是两个几乎完全平行的两个类,唯一不同的就是内部类要受到类域的限制,使用时必须在外部类类域下使用,同时内部类天生是外部类的友元。

        ​​​​​​​        ​​​​​​​        ​​​​​​​

6. 匿名对象

        匿名对象就是在实例化的时候不给名字,它的生命周期只在当前这一行,即用即销毁

        ​​​​​​​        ​​​​​​​        

7. 拷贝对象时的一些编译器优化

        因为我的编译器是vs2022优化开的太大了,没办法演示,就简单描述一下了。优化的话如果在Debug版本下,一定是在一行代码中进行优化的。但是如果开成release版本就有可能产生跨行优化。我下面讲的都是在Debug版本下的编译器优化方案。

        隐式类型:连续构造+拷贝构造 -> 优化成直接构造

        传值返回:拷贝构造+拷贝构造 -> 优化为一个拷贝构造,这里有一个值得注意的点,就是拷贝构造+赋值运算重载是无法优化的

        

        

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

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

相关文章

Proxy 代理

意图 为其它对象提供一种代理以控制这个对象的访问。 结构 Proxy保存一个引用使得代理可以访问实体&#xff1b;提供一个与Subject的接口相同的接口&#xff0c;使代理可以用来替代实体&#xff1b;控制实体的存取&#xff0c;并可能负责创建和删除它&#xff1b;其他功能依赖…

【leetcode面试经典150题】63. 删除链表的倒数第 N 个结点(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

现货白银保证金交易要先分析趋势

现货白银是保证金交易品种&#xff0c;买卖过程中可能会涉及数十倍的资金杠杆&#xff0c;所以它对投资者的分析水平和交易水平的要求都比较高&#xff0c;所以在进入这个市场之前&#xff0c;投资者需要先学习一些基本的分析方法&#xff0c;当中可以分为基本面和技术面两大流…

Vmware ---快捷键

Vi 文件名.c xrandr 查看分辨率 xrandr -s 分辨率 调你自己想要的分辨率 ctr shift 放大字体 ctr - 缩小字体 ctr alt t 打开控制台 cd caoshupei 进入曹树培文件夹 cd .. 退回上层文件夹 ls 列出生成的文件 ls -a 显示所有文件&#xff0c;包含隐藏的文件和文件…

2024-4-狼道

2024-4-狼道 2024-4-9 宋犀堃&#xff08;堃通坤&#xff0c;多用于人名&#xff09; fatux&#xff1a; 做人当如狗&#xff0c;和蔼可亲&#xff1b;做事当如狼&#xff0c;专注果决。 狼道 智慧生存的强者法则 走向卓越的成功之道 狼道&#xff0c;是追求卓越的野心&am…

第一个Python程序

1、python与java的区别 Python和Java是两种不同的编程语言&#xff0c;它们具有以下一些主要区别&#xff1a; 语法&#xff1a;Python的语法相对简洁和可读性高&#xff0c;使用缩进来表示代码块&#xff1b;而Java的语法更为严格&#xff0c;使用花括号来表示代码块。 类型…

后端-MySQL-week11 事务

事务 简介 操作 有两种方式&#xff0c;一种是设置为手动提交——不执行“commit”不进行变更&#xff1b;另一种是手动开启一个事务&#xff0c;用开启事务的代码&#xff08;SQL语句&#xff09;来创建一个需要“commit”才能进行变更的事务 1.第一种方式 2.第二种方式 四…

会声会影2023序列号免费和激活下载(附注册机keygen下载)

安装前准备 1、会声会影2023的安装需要在保证电脑联网的状态下进行。请您确保安装过程中有良好的网络环境&#xff0c;并且在安装过程中不可断网。 2、安装之前&#xff0c;请先退出、关闭360、电脑管家以及杀毒软件。 会声会影2023安装和下载 1、选择您下载版本对应的会声会…

配置使用IPsec安全框架保护RIPng报文

正文共&#xff1a;999 字 11 图&#xff0c;预估阅读时间&#xff1a;1 分钟 前面我们简单介绍了IPv6的基本概念&#xff08;IPv6从入门到精通&#xff09;&#xff0c;也做了动态路由协议的相关介绍&#xff08;IS-ISv6配置&#xff09;&#xff0c;还做了一个综合性比较强的…

抓取内网windows密码和利用hash横向及相关问题

目录 实验准备 用msf拿到shell 抓取hash和明文密码 相关问题 问题1.通过hashdump抓取所有用户的密文为什么分成两个模块&#xff0c;这两个模块分别代表什么 &#xff1f; 问题2.为什么第一个模块 永远是一样的aad3&#xff1f; 问题3.这两个模块的加密算法有什么不同&a…

Android Jetpack学习系列——Room

关于Room&#xff1a; Room是Android Jetpack组件之一&#xff0c;旨在为Android应用程序提供一种简单、强大且易于使用的本地数据库访问解决方案。 关键特性&#xff1a; 1.基于SQLite封装&#xff1a;Room是基于SQLite数据库引擎构建的&#xff0c;提供了面向对象的API来与…

Postgres数据库中的死锁是如何产生的,如何避免和解决?

文章目录 死锁的产生原因如何避免死锁如何解决死锁示例代码查询死锁信息终止事务 在Postgres数据库中&#xff0c;死锁是一种特殊的情况&#xff0c;其中两个或多个事务相互等待对方释放资源&#xff0c;从而导致它们都无法继续执行。这种情况通常发生在多个事务尝试以不同的顺…

Clark Transform的FPGA代码实现讲解

Clark 变换是坐标转换&#xff0c;将输入的三相电流转换到两相直角坐标下电流&#xff0c;如下图为坐标表示方法。 根据坐标的投影我们可以得到 从而可以推知&#xff1a; 上述公式为最终代码中实现的计算公式。 在FPGA中实现时&#xff0c;由于FPGA中不擅长浮点数计算&#xf…

Leetcode刷题-(26~35)-Java

算法是码农的基本功&#xff0c;也是各个大厂必考察的重点&#xff0c;让我们一起坚持写算法题吧。 遇事不决&#xff0c;可问春风&#xff0c;春风不语&#xff0c;即是本心。 我们在我们能力范围内&#xff0c;做好我们该做的事&#xff0c;然后相信一切都事最好的安排就可…

数据结构之排序了如指掌(三)

目录 题外话 正题 快速排序 Hoare法 Hoare法思路 Hoare法代码详解 挖坑法 挖坑法思路 挖坑法代码 前后指针法 前后指针法思路 前后指针法代码 小结 题外话 我们接着把没有写完的排序内容完成,快速排序其实大同小异,大家好好把思路整理一下 正题 快速排序 快速排序一…

WideDeep

这里写目录标题 1. 背景2. 贡献3 模型结构&#xff08;1&#xff09;任务定义&#xff08;2&#xff09;The Wide Component&#xff08;3&#xff09;The Deep Component&#xff08;4&#xff09;联合训练Wide和Deep Model 4. 参考 1. 背景 (1) 广义线性回归通常被用于推荐模…

树莓派+Openwrt连接校园网,打破校园网设备限制

前言 因为本校学生校园网只允许最多三个设备登录&#xff0c;对于同时拥有多个联网设备的我十分不友好&#xff0c;而且大多单片机如esp32的wifi模块是只允许一般的WPA/WPA2认证的&#xff0c;是不支持校园网的portal认证。所以我决定搞一个路由器。 然后我上网买了一个TP-Li…

Android Studio 新建Android13 代码提示Build Tools revision XX is corrupted无法编译解决

Android Studio 新建Android13 代码提示Build Tools revision XX is corrupted无法编译解决 文章目录 Android Studio 新建Android13 代码提示Build Tools revision XX is corrupted无法编译解决一、前言二、分析解决1、原因分析2、解决方法 三、其他1、Android13 新项目无法编…

采用matplotlib可视化kitti

配置kitti_object_vis没成功&#xff0c;用kitti_object_vis的一些函数加上matplotlib进行可视化 import numpy as np import matplotlib.pyplot as pltimport numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D def roty(t):"&quo…

JavaWeb-登录校验

会话技术 浏览器使用的是http协议&#xff0c;多次请求间数据是不能共享的&#xff0c;例如我们要去访问用户数据的接口&#xff0c;但这时候用户是否已经登入了呢&#xff1f;是不知道的&#xff0c;为了解决这个问题&#xff0c;于是引入了会话跟踪技术。 会话&#xff1a;…