当前位置: 首页 > news >正文

《类和对象(下)》

引言:

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

一:再探构造函数

  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++了(开心),不过后面要学的东西还多呢(大声)
本篇完结撒花!!!

相关文章:

  • Android NDK 高版本交叉编译:为何无需配置 FLAGS 和 INCLUDES
  • Cursor 编辑器 的 高级使用技巧与创意玩法
  • Flask Docker Demo 项目指南
  • 二次封装 el-dialog 组件:打造更灵活的对话框解决方案
  • 六、Hive 分桶
  • Spark处理过程-转换算子
  • 运行Spark程序-在Spark-shell——RDD
  • 第四章 部件篇之按钮矩阵部件
  • 前端如何应对精确数字运算?用BigNumber.js解决JavaScript原生Number类型在处理大数或高精度计算时的局限性
  • JVM Optimization Learning(七)-GC
  • JVM——方法内联之去虚化
  • 哈希表:数据世界的超级索引
  • 【速通RAG实战:进阶】10.RAG 进化论:Advanced与Modular架构解锁智能问答新维度
  • Kafka 如何保证消息顺序性
  • 关于IDE的相关知识之二【插件推荐】
  • Kubernetes Horizontal Pod Autosscaler(HPA)核心机制解析
  • 数据结构基础--蓝桥杯备考
  • 【自学30天掌握AI开发】第1天 - 人工智能与大语言模型基础
  • 《医院网络安全运营能力成熟度评估指南》(试行版)研究解读
  • MapReduce基本介绍
  • 西安市未央区委书记刘国荣已任西咸新区党工委书记
  • 落实中美经贸高层会谈重要共识,中方调整对美加征关税措施
  • 人民日报访巴西总统卢拉:“巴中关系正处于历史最好时期”
  • 新闻1+1丨婚姻登记服务,如何跑出幸福加速度?
  • 大外交|中美联合声明拉升全球股市,专家:中美相向而行为世界提供确定性
  • 消费维权周报|上周涉手机投诉较多,涉拍照模糊、屏幕漏液等