类和对象(中)-- 类的六个默认成员函数

目录

1.构造函数

1.1构造函数的特性

1.2 默认构造函数

 1.3补丁

​2.析构函数

2.1析构函数的特性

2.2构造函数与析构函数的调用顺序

3.拷贝构造函数(复制构造函数)

3.1拷贝构造函数的特征

4.赋值运算符重载

4.1运算符重载

4.2类内重载运算符

4.3=赋值运算符重载

 4.4传值返回和引用返回

4.5其他运算符重载

4.6友元

 4.7Date类的实现

5.const成员函数

6.取地址及const取地址运算符重载


一个类中什么成员都没有,简称为空类,但空类中并非什么都没有,编译器会自动生成以下六种默认成员函数;
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

1.构造函数

对于上面的Date类,每次创建后都要调用init公有方法给实例化的对象进行初始化,有时候会有可能忘记初始化对象(如d2),所以c++有了构造函数,使对象在创建出来时就同时完成初始化:

构造函数:构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次(实例化对象时调用)

1.1构造函数的特性

1.用于初始化对象,并不是开空间创建对象
2.函数名与类名相同
3.对象实例化时编译器自动调用对应的构造函数
4.构造函数可以重载
5.无返回值

 (注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,跟了括号就变成了函数声明了)

 6.如果类中没有显式定义构造函数,则c++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义构造函数编译器将不再生成。

 7.编译器生成的默认构造函数有什么用?
前面的例子可以看出调用了编译器自动生成的默认构造函数,似乎并没有对_year,_month,_day进行初始化,依然是随机值。
解释:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,编译器生成默认的构造函数会对自定类型成员调用的它的默认成员函数,内置类型会不会处理看编译器(不处理或者处理为0),内置类型没有规定要处理,对于自定义类型会调用的默认构造函数(注意是默认的),如果没有默认构造就会报错,所以要有默认构造,也可以没有默认构造,要使用初始化列表 

 自定义类型嵌套自定义类型,嵌套的尽头还是内置类型,内置类型要处理,一般情况下构造函数需要我们显式得去实现,只有少数情况可以让编译器自动生成(比如自定义类型里面只有自定义类型,没有内置类型)

1.2 默认构造函数

默认构造函数有三种:无参的、编译器自动生成的、全缺省的
总的来说,就是不传参数就可以调用的函数就是默认构造函数
注意:三个不能同时存在(存在调用歧义的问题)

 1.3补丁

C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在
类中声明时可以给默认值,这个默认值用于初始化列表初始化。(并不是初始化,这里是声明)


2.析构函数

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由
编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作(动态开辟的空间)。

2.1析构函数的特性

1.析构函数名是在类名前面加上字符 ~ 
2.无参数无返回值类型
3.一个类只能有一个析构函数,若未显式定义,系统会自动生成默认的析构函数,需要注意:析构函数不能重载
4.对象生命周期结束时,c++编译系统会自动调用析构函数,析构函数可以手动调用,但这相当于两次调用了析构函数

5.析构函数没有显式定义,系统会自动生成默认析构函数,系统自动生成的默认析构函数会:
        a.内置类型不做处理
        b.自定义类型去调用它的析构函数
内置类型不做处理,这样就可能会造成内存泄露,但内存泄漏不会报错 ;

总结:
        a.有资源需要显式清理,就需要自己写析构;
        b.这两种场景可以不写析构,用编译器自动默认生成的就可以了:
                b1.没有资源需要清理
                b2.内置类型成员没有资源需要清理,且剩下的都是自定义类型成员

2.2构造函数与析构函数的调用顺序

 与压栈出栈的顺序有关。

3.拷贝构造函数(复制构造函数)

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰,即const typename& a),在用已存在的类类型对象创建新对象时由编译器自动调用

3.1拷贝构造函数的特征

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

c++存在规定:自定义类型传值传参需要调用拷贝构造:
假设存在Date类的对象d1,现在需要拷贝构造一个Date类的对象d2,将d1作为值传给拷贝构造函数,由于是自定义类型传值传参需要调用拷贝构造,所以d1值传给拷贝构造函数之前,我需要对d1调用拷贝构造函数……这样就引发了无穷递归调用了

 3.若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

 

注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请
时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用
尽量使用引用

 

4.赋值运算符重载

4.1运算符重载

c++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有返回值,函数名字以及参数列表,其返回值类型与普通的函数类似。
函数名字构成:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator符号 (参数列表)
作用:增强代码可读性

 .*运算符:        

例如:日期类>运算符重载,这是作为全局函数的实现方式
运算符重载可以显式调用,也可以转换调用,编译器会自动转换成显式调用

4.2类内重载运算符

将运算符重载成全局函数,无法访问类内私有成员变量,解决方法:
1.提供这些成员变量的get函数和set函数
2.设置成友元函数
3.类内重载运算符(一般使用这种)

重载成成员函数:

因为类内成员函数都有一个隐含的this指针参数在最前面,所以只需要写一个参数,转换调用格式相同。

注意:如果同类型的运算符重载,一个在类内,一个在全局,会优先调用类内的运算符重载函数

4.3=赋值运算符重载

参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回效率,有返回值目的是为了支持连续赋值
返回*this:符合连续赋值的含义
 

用户没用显式实现赋值运算符重载时,编译器会默认生成一个,以值的方式逐字节拷贝(浅拷贝)。(内置类型成员变量时直接赋值的,而自定义类型的成员变量需要调用对应类的赋值运算符重载完成赋值)

赋值运算符重载不能重载成全局的(赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

 4.4传值返回和引用返回

传值返回:以临时对象充当返回值(拷贝构造成临时对象)

 引用返回:返回对象的别名(注意返回对象出函数时生命周期是否结束)

虽然引用返回可以减少一次拷贝,但出了函数作用,返回对象还在,才能用引用返回

 总结:如果返回对象生命周期到了,会析构,用传值返回;返回对象生命周期没到,并不会析构,用传引用返回。

4.5其他运算符重载

前置++--,后置++--
后置++--运算符重载,为了区分前置++--,并构成重载,给后置++--强行增加一个int形参,int形参只是用来区分前置后置,没有实际意义


<<运算符重载和>>运算符重载

由于运算符重载中,参数顺序和操作顺序是一致的,<<(或者>>) 定义在类内,第一个形参是隐含的this指针,导致调用方式与常见的cout和int调用顺序不同。

 可以在类内成为成员函数,但调用形式不符合正常逻辑,所以不建议在类内重载,可以重载成全局函数

 返回值是ostream&是为了连续操作,如:cout << d1 << d2 << endl;
&是因为ostream(和istream)禁止了拷贝构造;
返回的对象是out(就是cout,out是cout的引用)
cin是istream类的全局对象
cout是ostream类的全局对象

以上操作都是在成员变量在public权限下的情况,但成员变量一般都在private权限下,那上述操作将无法访问,在类外访问私有需要友元

4.6友元

关键字:friend
友元函数不属于类内成员,只提供了访问类内私有成员的权限

 也可以类内定义:

 4.7Date类的实现

#pragma once

#include <iostream>
#include <cassert>
using namespace std;

class Date
{
	friend ostream& operator<<(ostream& out, const Date& d)
	{
		cout << d._year << '-' << d._month << '-' << d._day;
		return out;
	}
	friend istream& operator>>(istream& in, Date& d)
	{
		cin >> d._year >> d._month >> d._day;
		return in;
	}
		
public:
	Date(int year = 2025, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
		cout << "Date()" << endl;
	}
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		cout << "Date(const Date& d)" << endl;
	}
	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
		cout << "~Date()" << endl;
	}
	//判断
	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}
	bool operator<(const Date& d)
	{
		//复用>=
		return !(*this >= d);
	}
	bool operator<=(const Date& d)
	{
		//复用>
		return !(*this > d);
	}
	bool operator>(const Date& d)
	{
		if (_year > d._year)
		{
			return true;
		}
		else if (_year == d._year)
		{
			if (_month > d._month)
			{
				return true;
			}
			else if (_month == d._month)
			{
				return _day > d._day;
			}
		}
		return false;
	}
	bool operator>=(const Date& d)
	{
		//复用>和==
		return (*this > d) || (*this == d);
	}
	bool operator==(const Date& d)
	{
		return (_year == d._year) && (_month == d._month) && (_day == d._day);
	}
	bool operator!=(const Date& d)
	{
		//复用==
		return !(*this == d);
	}
	//增减天数
	int GetMonthDay(int year, int month)
	{
		//默认内联-频繁调用
		assert(month > 0 && month < 13);
		static int monthDayArray[13] = { -1, 31, 28,31,30,31,30,31,31,30,31,30,31 };
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
			return 29;
		else
			return monthDayArray[month];
	}
	Date& operator+=(int day)
	{
		if (day < 0)
		{
			return *this -= -day;
		}
		_day += day;
		while (_day > GetMonthDay(_year, _month))
		{
			_day -= GetMonthDay(_year, _month);
			++_month;
			if (_month == 13)
			{
				++_year;
				_month = 1;
			}
		}
		return *this;
	}
	Date operator+(int day)
	{
		Date tmp(*this);
		return tmp += day;
	}
	Date& operator-=(int day)
	{
		if (day < 0)
		{
			return *this += -day;
		}
		_day -= day;
		while (_day <= 0)
		{
			--_month;
			if (_month == 0)
			{
				--_year;
				_month = 12;
			}
			_day += GetMonthDay(_year, _month);
		}
		return *this;
	}
	Date operator-(int day)
	{
		Date tmp(*this);
		return tmp -= day;
	}
	int operator-(const Date& d)
	{
		int count = 0;
		int flag = 1;
		Date greater(*this);
		Date smaller(d);
		if (greater < smaller)
		{
			greater = d;
			smaller = *this;
			flag = -1;
		}
		while (greater != smaller)
		{
			smaller += 1;
			++count;
		}
		return count * flag;
	}
	Date& operator++()//++a
	{
		return *this += 1;
	}
	Date operator++(int)//a++
	{
		Date tmp(*this);
		*this += 1;
		return tmp;
	}
	Date& operator--()//--a
	{
		return *this -= 1;
	}
	Date operator--(int)//a--
	{
		Date tmp(*this);
		*this -= 1;
		return tmp;
	}


	void Print()
	{
		cout << _year << '-' << _month << '-' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

5.const成员函数

将const修饰的“成员函数”称为const成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改(原来的隐含this指针参数是Date* const this)

 const对象不能调用非const成员函数
非const对象可以调用const成员函数
const成员函数内可以调用其他的非const成员函数
非const成员函数内可以调用其他的const成员函数

总结:如果没有修改的必要,可以在函数后面都加上const

6.取地址及const取地址运算符重载

这两个默认成员函数一般不用重新定义,编译器会默认生成

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

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

相关文章

ChatGPT 深度解析:技术驱动的智能对话

在当今科技飞速发展的时代&#xff0c;ChatGPT 无疑成为了最耀眼的明星之一。它以其令人惊叹的智能对话能力&#xff0c;引发了全球范围内的广泛关注和热议。 ChatGPT 背后的技术堪称精妙绝伦。它基于深度学习算法&#xff0c;通过对海量数据的学习和分析&#xff0c;从而能够理…

浅谈Git

一&#xff1a;什么是 git git一种开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 下图是 git 的一个工作流程简图 二&#xff1a;什么是 分布/集中式版本控制系统 软件开发过程中&#xff0c;要解决多人协作的问题&#xff0c;需要…

【Linux】常见指令(下)

【Linux】常见指令&#xff08;下&#xff09; 通配符 *man指令cp指令echo指令cat指令&#xff08;简单介绍&#xff09;cp指令 mv指令alias指令which ctrl ccat指令linux下一切皆文件 more指令less指令head指令tail指令管道 通配符 ‘*’ 通配符’ *‘&#xff0c;是可以匹配…

Linux(CentOS7)离线安装Redis6

版本 CentOS-7、redis-6 1、下载redis离线包 下载地址&#xff1a;http://download.redis.io/releases/ 2、选择安装包 redis-6.2.5.tar.gz 3、上传安装包至服务器 cd /usr/local/redis #进入目录&#xff0c;没有redis目录则自行创建 tar -zxvf redis-6.2.5.tar.gz #…

【信息收集】域名信息收集

域名介绍 域名&#xff08;Domain Name&#xff09;&#xff0c;简称域名、网域&#xff0c;是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称&#xff0c;用于在数据传输时标识计算机的电子方位&#xff08;有时也指地理位置&#xff09;。 DNS&#xf…

解决RuntimeError: Couldn‘t load custom C++ ops. This can happen if your PyTorch

问题描述 刚下好yolov8的代码&#xff0c;想测一下能否成果&#xff0c;果然没成功&#xff0c;报错如下 RuntimeError: Couldnt load custom C ops. This can happen if your PyTorch and torchvision versions are incompatible, or if you had errors while compiling tor…

【python】Pandas运行报错分析:SettingWithCopyWarning及其处理

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

通用图形处理器设计GPGPU基础与架构(三)

一、前言 前两篇已经介绍了 GPGPU 的背景 和 GPGPU 的编程模型相关的内容&#xff0c;本文将在 SIMT 计算模型的基础上&#xff0c;介绍 GPGPU 控制核心架构和微体系结构的设计。 二、CPU-GPGPU 异构计算系统 一个由 CPU 和 GPGPU 构成的异构计算平台如下图所示&#xff0c;GP…

《昇思25天学习打卡营第23天|ResNet50迁移学习》

文章目录 ResNet50迁移学习数据准备下载数据集 加载数据集数据集可视化 训练模型构建Resnet50网络固定特征进行训练训练和评估可视化模型预测 总结打卡 ResNet50迁移学习 在实际应用场景中&#xff0c;由于训练数据集不足&#xff0c;所以很少有人会从头开始训练整个网络。普遍…

摸鱼大数据——Kafka——kafka tools工具使用

可以在可视化的工具通过点击来操作kafka完成主题的创建&#xff0c;分区等操作 注意: 安装完后桌面不会有快捷方式,需要去电脑上搜索,或者去自己选的安装位置找到发送快捷方式到桌面! 连接配置 创建主题 删除主题 主题下的数据查看 数据显示问题说明 修改工具的数据显示类型 发…

Laravel :如何将Excel文件导入数据库

文章目录 一、前提二、使用2.1、新建一个导入文件2.2、新建一个控制器和方法,调用导入文件2.3、 新建一个页面&#xff0c;支持文件上传 一、前提 想要将excel内容入库&#xff0c;laravel有扩展可以使用,常用的扩展是maatwebsite/excel&#xff0c;安装步骤参考上一篇&#x…

校园工会体育报名小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;赛事公告管理&#xff0c;球员管理&#xff0c;球队信息管理&#xff0c;比赛信息&#xff0c;比赛报名管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;比赛信息&#xff0c;比赛报名&#…

7月考研数学的保底进度,警惕三个误区!

误区1. 不恰当的课程选择和学习计划 尤其25张宇36讲大改版&#xff0c;一些同学感到焦虑&#xff0c;担心自己的课程选择不适合自己。 或者担心学习计划不够高效&#xff0c;影响最终的成绩。 课程选择&#xff0c;看3方面&#xff1a; 1. 覆盖是否全面&#xff1f; 2. 是否…

element-ui dialog 嵌套

dialog 内部嵌套 dialog&#xff0c;内层的 dialog 层级显示会遮罩在内容的 dialog 内容区域之上&#xff0c;内层 dialog 添加 append-to-body 属性即可&#xff0c;如官方文档&#xff1a;

网安小贴士(11)VPN类型

前言 VPN&#xff08;Virtual Private Network&#xff0c;虚拟专用网络&#xff09;类型多样&#xff0c;主要根据其使用的协议、应用场景以及实现方式等因素进行分类。以下是对VPN类型的详细概述&#xff1a; 一、按协议分类 根据使用的隧道协议&#xff0c;VPN可以分为以下几…

java设计模式(十五)命令模式(Command Pattern)

1、模式介绍&#xff1a; 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;其主要目的是将请求封装成一个对象&#xff0c;从而允许使用不同的请求、队列或者日志来参数化其他对象。这种模式使得命令的请求者和实现者解耦。 2、应用场景&…

c++树笔记

树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个结点的有限集。n0时称为空树。在任意一颗非空树中&#xff1a;①有且仅有一个特定的称为根&#xff08;Root&#xff09;的结点&#xff1b;②当n>1时&#xff0c;其余结点可分为m&#xff08;m&g…

基于 jenkins 部署接口自动化测试项目!

引言 在现代软件开发过程中&#xff0c;自动化测试是保证代码质量的关键环节。通过自动化测试&#xff0c;可以快速发现和修复代码中的问题&#xff0c;从而提高开发效率和产品质量。而 Jenkins 作为一款开源的持续集成工具&#xff0c;可以帮助我们实现自动化测试的自动化部署…

itextpdf字体选择

itextpdf 版本7.2.5 itextpdf-html2pdf 版本4.0.5 这里讲的是通过html转pdf&#xff0c;在html2pdf中是通过html中font-family样式来确定字体的&#xff0c;那已知font-family的情况&#xff0c;怎么确定pdf中实际用的字体&#xff0c;大致分为两步&#xff1a; 1、通过font…

ollama 模型国内加速下载,制作自定义Modelfile模型文件

参考: https://www.zhihu.com/question/640579563/answer/3562899008 https://github.com/ollama/ollama/blob/main/docs/modelfile.md gguf格式介绍: https://www.datalearner.com/blog/1051705718835586 1、ollama 模型国内加速下载 ollama主要的模型文件格式是gguf,可…