Learning C++ No.5【类和对象No.4】

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

引言

北京时间2023/2/6/1045今天起床时间10:00睡迟了可能是因为昨天睡的有点晚但是庆幸的是昨天没人叫我玩鹅鸭杀我们把博客给更新了哈哈哈所以今天我们趁热打铁一鼓作气把类和对象给搞定。但是伴随着开学的钟声快要响起考试即将来临说不慌是假但是我就是没有复习的动力可能是上课太过于摆烂连复习什么我都不知道也可能是我觉得小小的考试60分并难不倒我也可能是我对C++的学习比较上头。反正综合现在来看C++对我的吸引比较强。所以车道山前必有路开学干的事情开学再说现在是我自己的快乐时间我们今天就来把有关类和对象中的重点知识运算符重载以及相关知识学习一下。

我们在前面学习了构造函数析构函数拷贝构造函数以及相关的知识之后此时我们就利用这些知识来实现一个计算日期的日期类代码。意思为在一个日期上加上天数然后得到加上天数之后的日期

代码如下也算是复习和运用之前的各种知识

深入拷贝构造

从上篇博客中我们知道了什么是拷贝构造并且大致学会了如何使用拷贝构造此时我们就再来看看拷贝构造中的一些细节方面。
如下代码

如图所示我们可以发现拷贝构造是会直接对内置类型进行初始化的其实按照昨天的知识我们知道拷贝构造其实就是一个构造函数但是它初始化的数据是从别的对象中复制过来的而已。但是拷贝构造和构造函数两者又有一点的不同因为构造函数只会对自定义类型进行初始化而不会对内置类型进行初始化但我的拷贝构造函数却不仅会对内置类型进行初始化也会对自定义类型进行初始化代码还没有演示所以拷贝构造和构造函数在某些方面还是有很大的不同的。并且由于我们的拷贝构造函数是会自动对内置类型进行初始化的所以以后我们在写内置类型的时候是不需要去自我实现拷贝构造函数的编译器会自己去调用拷贝构造函数。当然前提是我拷贝的对象已经被构造函数初始化过这样才可以实现自己不实现拷贝构造而使我的创建对象被初始化。

接着上面的内容我们来看一下拷贝构造是如何对自定义类型进行初始化的如下代码我们会发现一个很重要的问题

我们发现我们在进行拷贝构造的时候确实是不需要自己去实现拷贝构造函数的编译器会自己去调用编译器系统内部的拷贝构造函数从而实现内容数据的拷贝但我们此时还会发现我们的自定义类型中虽然内容被拷贝了但此时int* _a指向了一块空间编译器指向拷贝构造之后导致st2对象中的int*_a此时也被初始化成指向了st1指向的那块空间从图中的两个内存地址相同可以看出所以现在就会有一个致命问题需要我们解决就是深拷贝问题并且可以发现我们的编译器虽然会自动调用拷贝构造但是该拷贝构造只是一个浅拷贝编译器没有执行深拷贝的能力所以当我们遇到自定义类型需要进行拷贝的时候无论是浅拷贝还是深拷贝此时编译器是不敢乱自动去调用系统内部的拷贝构造编译器此时必须优先调用我们自己实现的拷贝构造函数而不是自动调用编译器系统内部的拷贝构造函数。

从深浅拷贝问题深入拷贝构造

所以如果按照上述的代码我们没有自己去实现拷贝构造函数而是让编译器去自动调用系统内部的拷贝构造那么此时程序最终会崩溃因为当程序运行到最后是会去调用我们的析构函数让析构函数帮我们把内部的空间资源给清理掉然而此时因为st1st2都存储在main函数的栈帧上那么此时main函数栈帧在销毁前会对st1st2进行清理并且按照栈帧原则先进后出此时是先对st2这个后定义的对象清理再对st1这个先定义的对象清理但st1st2对象中的int*_a指向了同一块空间那么在清理是导致st2int*_a先被清理清理就是析构函数释放内存并置成空指针重点虽然st2在清理时将int*_a指向的空间置成了空指针但是该空间的变化是针对于st2这个对象空间此时并不会影响到st1对象的空间如下图所示此时就让我们带着这个问题真正的进入到内存之中去看待深浅拷贝问题此时我们参考如下代码从代码方面去看看深浅拷贝和内存之间的关系。通过深浅拷贝问题来解决拷贝构造的问题。

此时当我们程序要结束时就是执行到了return我们的编译器会自动调用我们的析构函数来清理资源此时我们发现析构函数是清理我们的st2并且可以发现当析构函数执行完之后我们的int*_a指针确实指向了空但我们的st1int*_a指针指向的却并不是空。所以通过这个图我们确实是可以知道清理st2对象的空间是不会影响到st1的空间的但是我们发现我们的程序却依然还是崩溃了如下图

这是什么原因呢为什么我的程序还是会崩溃呢原因就是我的st2对象中的int*_a指针指向的空间被释放之后导致st1中的int*_a指针指向的空间被释放此时就导致st1中的int*_a指针指向了一块随机的内存空间就是变成了野指针所以此时程序就因为野指针问题导致崩溃了所以当两个不一样的指针指向了同一块空间之后此时是会造成很大的问题的所以为了避免造成两个指针指向同一块空间的问题我们的编译器是不允许直接调用系统编译器内部的拷贝构造函数必须优先调用我们自己实现的拷贝构造函数只有这样才可以区分深拷贝和浅拷贝问题。

总指向同一块空间的问题插入和删除数据会互相影响并且导致该空间会析构两次程序崩溃。所以不允许编译器自动调用系统编译器内部的拷贝构造而是优先调用自己实现的拷贝构造。

并且拷贝构造的典型使用场景有

使用已存在对象创建新对象
函数参数类型为类类型对象
函数返回值类型为类类型对象

见见猪跑看一下到底什么是深拷贝如下图

这样我们就把深拷贝和拷贝构造直接的一些不间接关系给搞清楚了但此时我们会有一个疑问那就是我们什么时候需要自己写拷贝构造什么时候不要写拷贝构造呢通过上述知识可能有的同学就会想只要当我们有指针指向另一块空间的时候就要写拷贝构造没有的时候就不要这个是有一定道理的但是不是绝对的所以我们一般在我们自己实现了析构函数的时候我们就需要自己实现一个拷贝构造这个是很有道理的。

总当我们自己实现了析构函数的时候我们就要自己再实现一个拷贝构造不然就会出大问题。

什么是运算符重载

运算符重载C++为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有其返回值类型函数名字以及参数列表其返回值类型与参数列表与普通的函数类似。函数名字为关键字operator后面跟上需要重载的运算符符号。函数原型返回值类型operator操作符参数列表bool operator==(const Date& d1, const Date& d2)
注意

不能通过连接其他符号来创建新的操作符比如operator@
重载操作符必须有一个类类型参数
用于内置类型的运算符其含义不能改变例如内置的整形+不能改变其含义
作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏this
.* :: sizeof ?: . 这五个运算符不能被重载。

搞明白了上述的基础知识此时我们就可以真正的进入到运算符重载的学习了首先搞定的第一点我们想到C语言中都没有什么运算符重载的概念为什么C++中却有这个运算符重载的概念呢强调我们一直秉承着C++就是为了弥补C语言的不足而开创的所以自然而然运算符重载这个概念的提出就是为了可以补充一些C语言中的不足所以接下来我们就介绍一下运算符重载的好处和无运算符重载的坏处同时我们都是以日期类代码来实现的。

如下图

了解完了我们为什么要引入运算符重载的这个概念此时我们就深入其内部深入学习运算符重载和其相关的一些知识首先我们可以知道运算符重载函数不仅可以放在全局中也可以放在我们的类中并且将运算符重载函数放到了类中之后此时就如上述注意中的第3点所说其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏this。并且使用运算符重载就是把运算符重载的函数理解成是一个正常的函数只是名字比较特殊一点点而已。当我们定义运算符重载函数的时候运算符的返回值是bool值真返回1假返回0参数由运算符的操作数决定该运算符有几个操作数就有几个参数,并且第一个参数就是左操作数第二个参数就是右操作数。所以知道了这些对运算符重载函数的基本使用之后我们就看一下运算符重载函数是如何在我们的类中使用的。

如图所示

运算符函数的复用

搞定了运算符重载函数在类中的使用我们现在来学一下运算符函数的复用是如何实现的如下代码所示

赋值重载函数

首先我们的赋值重载函数是一个默认成员函数它的特性满足我们上述所学的有关默认成员函数的所有特性这里就不多加赘述了如图所示就是我们的复制运算符重载函数的实现

重点我们这边再来了解一下赋值重载和拷贝构造Date d1 =d2; 和 d1 = d2;之间的区别首先Date d1 =d2这个是拷贝构造这个是d1 = d2;赋值重载为什么呢原因就是赋值重载是对两个已经实例化完成了的对象而我的拷贝构造则是用一个实例化好的对象对另一个为实例化的对象进行初始化。

总结北京时间2023/2/7/1:20不困但是我只知道我熬夜了我又要长痘了呜呜呜上述我们把类和对象中的赋值重载和六大默认函数中的拷贝构造和赋值重载函数给重点学习了一下发现这些东西并不难学只是细节不好处理而已所以小命要紧撤

今天我的书到了此处纪念一下希望以后的我看到的时候我能对得起这本书不会把它放在角落里。当然我自己是很希望自己可以把这本书给看完了加油

在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: c++