【C++】---类和对象(中)默认成员函数 和 操作符重载

前言:

假如一个类中既没有成员变量也没有成员函数,那么这个类就是空类,空类并不是什么都没有,因为所有类都会生成如下6个默认成员函数:
在这里插入图片描述

一、构造函数

1、构造函数的定义及其特性

对于日期类对象,我们可能会忘记调用Init函数进行初始化,C++为了解决这个问题,引入构造函数进行初始化。

#include<iostream>
using namespace std;class Date
{
private:int _year;int _month;int _day;
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
};int main()
{Date d1;d1.Init(2024, 2, 11);d1.Print();return 0;
}

构造函数一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次
特性:
1.构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
2.其特征如下:

  • 函数名与类名相同。
  • 无返回值。
  • 对象实例化时编译器自动调用对应的构造函数。
  • 构造函数可以重载
  • 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义了编译器将不再生成!

3.默认构造函数是我们不传参就可以调用的函数

  • 我们什么都没写,编译器自动生成的
  • 我们自己写的:无参的构造函数
  • 我们自己写的:全缺省构造函数
    这三类只能存在一个,注意后两个:不能同时存在的原因:当定义一个不带参数的类对象时,编译器不能确定到底要调用我们写的无参默认构造函数还是要调用我们写的带参全缺省默认构造函数,会报“对重载函数的调用不明确错误”。
    (1) 我们什么都没写,编译器自动生成的
#include<iostream>
using namespace std;class Date
{
private:int _year;int _month;int _day;
};int main()
{Date d1;//调用编译器自动生成的默认构造函数return 0;
}

(2)我们自己写的:无参的构造函数

#include<iostream>
using namespace std;class Date
{
public://1.无参默认构造函数:初始化对象Date(){_year = 2024;_month = 2;_day = 12;}private:int _year;int _month;int _day;
};int main()
{Date d1;return 0;
}

(3)我们自己写的:全缺省构造函数

#include<iostream>
using namespace std;class Date
{
public://2.带参全缺省默认构造函数:初始化对象Date(int year = 2024, int month = 2, int day= 12){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};int main()
{Date d1;//调用带参默认构造函数return 0;
}

2、编译器自动生成的默认构造函数

关于编译器生成的默认成员函数,很多童鞋会有疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??
解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,
编译器生成默认的构造函数对内置类型不做处理
而对自定义类型成员会调用的它的默认成员函数

看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数:

class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
};
int main()
{Date d;return 0;
}

二、析构函数

1、析构函数的定义及其特性

析构函数用来完成类的资源清理工作,编译器在销毁对象时,会自动调用析构函数。
特性:

(1)析构函数名是在类名前加上字符 ~。

(2)无参数无返回值。(析构函数不能重载,一个类有且仅有一个析构函数)

(3)一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数

(4)对象生命周期结束时,C++编译系统系统自动调用析构函数。

2、多对象的析构顺序

假如这个类有多个对象,那么析构的先后顺序是什么?
多对象的析构顺序 :局部对象(后定义先析构) -> 局部的静态(后定义先析构) ->全局对象(后定义先析构)

// 局部对象(后定义先析构) -> 局部的静态(后定义先析构) ->全局对象(后定义先析构)
class Date
{
private:int _year;int _month;int _day;
public:Date(int year){_year = year;}~Date(){// 调用一次 析构函数 我就打印一次cout << "~Date()->" << _year << endl;}
};void func()
{// 局部域Date d4(4);static Date d5(5);static Date d10(10);
}// 全局域
Date d6(6);
static Date d7(7);
Date d8(8);
static Date d9(9);int main()
{// 局部域Date d1(1);Date d2(2);static Date d3(3);func();return 0;
}

3、编译器自动生成的默认析构函数

当不写析构函数时,编译器会自动生成默认的析构函数,不过这个默认的析构函数什么也不做,不需要清理资源。那么编译器自动生成的默认析构函数到底有什么用呢?

同析构函数

(1)对于内置类型,不会处理

(2)对于自定义类型,会调用它的析构函数

三、拷贝构造函数

1、拷贝构造函数定义及特性

1.拷贝构造函数只有单个形参,该形参是对同类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
2.特征:
拷贝构造函数也是特殊的成员函数,其特征如下:

  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错因为会引发无穷递归调用

思考:为什么使用传值方式会引发无穷递归调用?
C++规定:对自定义类型的函数传值传参时,都会调用拷贝构造函数!!!
在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
#include <iostream>using namespace std;class Date
{
private:int _year;int _month;int _day;
public:void Print(){cout << _year << "/" << _month << "/" << _day << endl;}// 构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//拷贝构造函数//Date(Date d)// 错误写法 使用传值方式编译器直接报错,因为会引发无穷递归调用Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}// 析构函数~Date(){}
};int main()
{Date d1(2024,2,12);Date d2(d1);d1.Print();d2.Print();return 0;
}

拷贝构造函数也是构造函数,函数名和类型名相同,参数是同类型对象的引用,由编译器自动调用。
因此,对于自定义类型的对象,一般推荐使用传引用传参,虽然传值传参也可以,但是要调用拷贝构造

(1)对其拷贝构造函数的理解

#include <iostream>
using namespace std;class Date
{
private:int _year;int _month;int _day;
public:void Print(){cout << _year << "/" << _month << "/" << _day << endl;}Date(int year = 2024, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 析构函数~Date(){cout << "~Date()" << endl;//在析构函数内打印,调用一次就打印一次}//拷贝构造函数Date(const Date& d)  // Date (Date* this ,Date &d)// 因为d2调用拷贝构造函数,所以&d2=this, (d是d1的别名){_year = d._year;_month = d._month;_day = d._day;}};int main()
{Date d1(2024,2,16);// 调用构造函数Date d2(d1);// 调用拷贝构造函数,就是在定义对象的时候直接用拷贝构造函数// Date(&d2,Date& d1)d2.Print();return 0;
}

在这里插入图片描述

2、深浅拷贝

1.传值传参:是浅拷贝
浅拷贝的缺点:
两个数组的指针指向同一块空间,当进行free时只能 free一个,那么另外一个就会变成“野指针”!
动态开辟资源的,浅拷贝都不行,因为浅拷贝只会仅仅copy数据。
在这里插入图片描述
2.传引用传参:是深拷贝:深拷贝还需要我们自己去写,编译器不会自动生成。
在这里插入图片描述
在这里插入图片描述
在判断一个类里面需不要写拷贝构造函数,根据具体情况而定。如果没有动态开辟的程序,就不需要写深拷贝。

3、编译器自动生成的拷贝构造函数

若未显式定义,系统会生成默认拷贝构造函数。 同构造函数和析构函数不同:

(1)拷贝构造函数对内置类型依次按照字节序完成拷贝,即浅拷贝或值拷贝。

假如不写拷贝构造函数,照样正常打印:

#define _CRT_SECURE_NO_WARNINGS 1 
#include <stdio.h>
#include <iostream>using namespace std;class Date
{
private:int _year;int _month;int _day;
public:void Print(){cout << _year << "/" << _month << "/" << _day << endl;}// 构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}拷贝构造函数Date(Date d)// 错误写法 使用传值方式编译器直接报错,因为会引发无穷递归调用//Date(const Date& d)//{//	_year = d._year;//	_month = d._month;//	_day = d._day;//}// 析构函数~Date(){}
};int main()
{Date d1(2024,2,12);Date d2(d1);d1.Print();d2.Print();return 0;
}

在这里插入图片描述

四、赋值运算符重载函数

1、运算符重载

1.定义:对于内置类型来说,语言层面本身就支持已经定义好的运算符,但对于自定义类型来说不行, C++中规定运算符重载的原因是:让自定义类型可以像内置类型一样使用运算符,想重载哪个运算符就重载哪个运算符。
运算符重载的语法:
函数原型:返回值类型 operator操作符(参数列表)
2.特性:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this指针
  5. (.*) (::) (sizeof) (?;) (.) 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

运算符重载实例:

(1)==运算符重载

#include<iostream>
using namespace std;class Date
{
public://构造函数Date(int year = 2024, int month = 2, int day = 16){_year = year;_month = month;_day = day;}//成员变量公有
public:int _year;int _month;int _day;
};//operator==运算符重载
bool operator==(Date x1, Date x2)
{return x1._year == x2._year&& x1._month == x2._month&& x1._day == x2._day;
}int main()
{Date d1(2024, 2, 15);Date d2(2024, 2, 16);//两种调用方式://1.可读性不强operator==(d1, d2);//2.当编译器看到==自定义类型,会去检查日期类有没有==的重载运算符,如果有重载会转换成operator==(d1, d2)去调用operator==函数d1 == d2;return 0;
}

2、运算符重载和函数重载的区别:

运算符重载和函数重载,虽然都使用了重载,但是两者之间没有关联:

(1)函数重载时支持定义同名函数

(2)运算符重载是为了让自定义类型可以像内置类型一样去使用运算符。

3、赋值运算符重载

1.赋值运算符重载语法格式:

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义

赋值运算符重载和拷贝构造的区别:

(1)拷贝构造函数:对即将要创建的新对象进行初始化(不过初始化的内容是将一个已经存在的对象拷贝给他)

(2)赋值运算符重载:两个已经存在的对象进行赋值

2.赋值运算符重载代码 以及如何调用!

class Date
{
private:int _year;int _month;int _day;
public:void Print(){cout << _year << "/" << _month << "/" << _day << endl;}Date(int year = 2024, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 析构函数~Date(){cout << "~Date()" << endl;//在析构函数内打印,调用一次就打印一次}//拷贝构造函数Date(const Date& d)  // Date (Date* this ,Date &d)// 因为d2调用拷贝构造函数,所以&d2=this, (d是d1的别名){_year = d._year;_month = d._month;_day = d._day;}//赋值运算符重载// d1=d2   //d1.operator=(&d1,d2)Date& operator=(const Date& d) // void Date& operator=(&d1,const Date& d){if (this != &d) // 对d取地址,判断this的值和d的地址是否相同,如果不是自己给自己赋值,才需要拷{_year = d._year;_month = d._month;_day = d._day;}return *this;}};int main()
{//Date d1(2024,2,16);// 调用构造函数//Date d2(d1);// 调用拷贝构造函数,就是在定义对象的时候直接用拷贝构造函数 Date(&d2,Date& d1)//d2.Print();Date d1(2024, 2, 16);d1.Print();Date d2;d2.Print();d2 = d1;d2.Print();return 0;
}

在这里插入图片描述

4、const修饰类的成员函数

定义:将const修饰的类成员函数称为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

比如有如下场景:假如把Date类的operator==运算符重载函数写错了

bool operator==(const Date& d)
{return (_year == d._year)&& (_month == d._month)&& (_day == d._day);
}

将其中的一个"==“错写成”=" :

bool operator==(const Date& d) //bool operator==(Date* this,const Date& d)
{return (_year == d._year)&& (_month = d._month)&& (_day == d._day);
}

虽然编译没有问题,但是这会导致this的值被修改了,并且执行结果也错误:

int main()
{Date d1(2024, 2, 16);Date d2(2024, 3, 16);cout << (d1 == d2) << endl;d1.Print();d2.Print();return 0;
}

这不符合要求,仅仅是比较而已,但是被比较对象的值却被修改了。const最大的作用是保护对象和变量,d2传给了d,d是d2的别名,const已经保护了d,那d1如何保护呢?由于this是隐含的,那么const为了保护this,应该如何加?把const加在成员函数的后面,叫做const修饰成员函数

bool operator==(const Date& d) const
{return (_year == d._year)&& (_month = d._month)&& (_day == d._day);
}

总结:
1.const引用:我是你的别名,但我不能修改你(是之前:权限的缩小)
比如:const int& c = a; // c是a的别名,但由于const修饰c所以说不能通过c来修a的值。
2.如果有const的修饰,那么说明这个变量是只能读的!如果没有const修饰,普通的变量它是可读可写的!
在这里插入图片描述

五、总结:

1.构造函数和析构函数
如果我们不写,编译器对内置类型不做处理,自定义类型会调用它的构造函数和析构函数进行处理

2.拷贝构造和赋值运算符重载
如果我们不写,内置类型会完成浅拷贝,自定义类型会调用它的拷贝构造函数和赋值运算符重载函数。

3.取地址操作符重载和const取地址操作符重载
一般不需要重载,编译器默认生成的已经够用,重载没有价值。


好了,今天的分享就到这里了
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!
在这里插入图片描述

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

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

相关文章

基于matlab的密度散点图绘制

1. 什么是密度散点图&#xff1f; 密度散点图就是在普通散点图的基础上&#xff0c;基于样本点一定范围的样本数计算该样本点的密度&#xff0c;以不同的颜色来显示样本点密度的大小&#xff0c;这样能够直观的显示出数据的空间聚集情况&#xff0c;如下图分别是二维和三维密度…

【教程】C++语言基础学习笔记(六)——String字符串

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【C语言基础学习】系列文章 第一章 《项目与程序结构》 第二章 《数据类型》 第三章 《运算符》 第四章 《流程控制》 第五章…

[word] word保存了但是再打开就没有了怎么办 #职场发展#其他

word保存了但是再打开就没有了怎么办 word保存了但是再打开就没有了怎么办&#xff1f; 一些朋友反映常常找不到自己保存在电脑中的Word的文档&#xff0c;不知道是怎么回事。如果是突然消失的&#xff0c;其实情况还是有很多种&#xff0c;相信大家也有一定的了解。在这里&a…

阿里云幻兽帕鲁Linux 服务器下载游戏存档的方法

阿里云幻兽帕鲁Linux 服务器下载游戏存档的方法也非常简单。 远程连接到阿里云的 linux服务器后&#xff0c;可以在 ECS 远程连接命令行界面&#xff0c;点击左上角的文件&#xff0c;打开文件树。通过一行命令打包。 在打包后的 Saved.tar 文件上右键&#xff0c;选择 下载文…

【python】python入门(输出)

本篇文章将会介绍关于python的常见输出&#xff0c;希望对您有帮助&#xff01; 输出 用到print函数 print(oh mygod)##或者 print("oh mygod")##或者 print("oh"" ""mygod") 输出结果&#xff1a; 用单引号、双引号都可以 ,引号中可…

下一代Windows系统曝光:基于GPT-4V,Agent跨应用调度,代号UFO

下一代Windows操作系统提前曝光了&#xff1f;&#xff1f; 微软首个为Windows而设的智能体&#xff08;Agent&#xff09; 亮相&#xff1a; 基于GPT-4V&#xff0c;一句话就可以在多个应用中无缝切换&#xff0c;完成复杂任务。整个过程无需人为干预&#xff0c;其执行成功…

【Qt】qt常用控件之QIcon 以及 qrc机制设置图片路径(QtCreator)

文章目录 1. QIcon / windowIcon2. setIcon() 与 setwindowIcon()2.1 setIcon() 介绍与使用2.2 setWindowIcon 介绍与使用 3. 路径问题 & qrc机制的引入3.1 绝对路径 / 相对路径 的问题3.2 qrc机制3.3 在QtCreator下利用qrc机制引入图片 1. QIcon / windowIcon QIcon QIco…

PR:时间重映射

做一个变换视频速度的效果 原片如下&#xff1a; 现在将跑步的人中间一段加速&#xff0c;后面一段减速 操作如下&#xff1a; 此处点击关键帧时&#xff0c;可以用钢笔工具&#xff0c;也可以按住Ctrl键点击 操作后效果如下&#xff1a;

Write operation failed: computed value is readonly问题解决

源代码&#xff1a; // 封装倒计时逻辑函数 import { computed, ref } from vue import dayjs from dayjs export const useCountDown () > {// 1.响应式数据const time ref(0)// 格式化时间const formatTime computed(()>dayjs.unix(time.value).format(mm分ss秒))/…

最短路径与关键路径

目录 文章目录 前言 一.最短路径 1.基本概念 1.1什么是源点&#xff1f; 1.2什么是最短路径 2.作用 3.迪杰斯特拉算法 4. 弗洛伊德算法 4.1过程演示 二.拓扑排序 1.基本概念 1.1什么是有向无环图 1.2什么是活动 1.3什么是AOV网 1.4什么是拓扑序列 1.5什么是拓扑…

Ubuntu 23.10通过APT安装Open vSwitch

正文共&#xff1a;888 字 8 图&#xff0c;预估阅读时间&#xff1a;1 分钟 先拜年&#xff01;祝各位龙年行大运&#xff0c;腾跃展宏图&#xff01; 之前在介绍OpenStack的时候介绍过&#xff08;什么是OpenStack&#xff1f;&#xff09;&#xff0c;OpenStack是一个开源的…

Python slice函数

在Python编程中&#xff0c;slice&#xff08;切片&#xff09;操作是一种强大且灵活的方式&#xff0c;用于从序列&#xff08;如列表、元组、字符串等&#xff09;中获取子序列。通过切片操作&#xff0c;可以轻松地提取序列中的一部分&#xff0c;进行遍历、修改、复制等操作…

指针习题回顾(C语言)

目录 数组指针和指针数组 编程题&#xff1a; 字符串逆序 字符串左旋 题目1概述&#xff1a; 代码实现&#xff1a; 题目2概述&#xff1a; 代码实现&#xff1a; 调整奇偶顺序 题目概述&#xff1a; 代码实现&#xff1a; 冒泡排序 二级指针 代码解读&#xff1a; …

【AIGC】Stable Diffusion的插件入门

一、上文中作者使用插件包的方式下安装插件&#xff0c;用户也可以从Stable Diffusion的界面安装插件&#xff0c;如下图所示&#xff0c;在相应的插件后面点安装按钮。 二、介绍一些比较好用的插件 “adetailer” 插件是 Stable Diffusion 中的一个增强功能&#xff0c;旨在提…

【Pygame手册02/20】pygame模块display控制窗口和屏幕

目录 一、说明二、pygame.display接口函数2.1 函数表格2.2 pygame.display的功能 三、详细的函数调用3.1 pygame.display.init()3.2 pygame.display.quit()3.3 pygame.display.get_init()3.4 pygame.display.set_mode()3.5 pygame.display.get_surface()3.6 pygame.display.fl…

飞天使-k8s知识点18-kubernetes实操3-pod的生命周期

文章目录 探针的生命周期流程图prestop 探针的生命周期 docker 创建&#xff1a;在创建阶段&#xff0c;你需要选择一个镜像来运行你的应用。这个镜像可以是公开的&#xff0c;如 Docker Hub 上的镜像&#xff0c;也可以是你自己创建的自定义镜像。创建自己的镜像通常需要编写一…

【AIGC】Stable Diffusion的采样器入门

在 Stable Diffusion 中&#xff0c;采样器&#xff08;Sampler&#xff09;是指用于生成图像的一种技术或方法&#xff0c;它决定了模型如何从潜在空间中抽样并生成图像。采样器在生成图像的过程中起着重要作用&#xff0c;影响着生成图像的多样性、质量和创造性。以下是对 St…

为自监督学习重构去噪扩散模型

在这项研究中&#xff0c;作者检验了最初用于图像生成的去噪扩散模型&#xff08;DDM&#xff09;的表示学习能力。其理念是解构DDM&#xff0c;逐渐将其转化为经典的去噪自动编码器&#xff08;DAE&#xff09;。这一解构过程让大家能够探索现代DDM的各个组成部分如何影响自监…

python自学...

一、稍微高级一点的。。。 1. 闭包&#xff08;跟js差不多&#xff09; 2. 装饰器 就是spring的aop 3. 多线程

《合成孔径雷达成像算法与实现》Figure6.17

% rho_r c/(2*Fr)而不是rho_r c/(2*Bw) % Hsrcf exp函数里忘记乘pi了 clc clear close all参数设置 距离向参数设置 R_eta_c 20e3; % 景中心斜距 Tr 2.5e-6; % 发射脉冲时宽 Kr 20e12; % 距离向调频率 alpha_os_r 1.2; …