运算符重载(下)

目录

  • 前置++和后置++重载
    • 前置++的实现
      • Date& Date::operator++()代码
    • 后置++的实现
      • Date Date::operator++(int )代码
  • 前置--和后置--重载
    • 前置--的实现
      • Date& Date::operator--( )代码
    • 后置--的实现
      • Date Date::operator--(int )代码
  • 流插入运算符重载
    • 流插入运算符重载的实现
    • 流提取运算符重载的实现
    • 日期类的检查函数
  • const成员函数
    • const对象不可以调用非const成员函数
    • 非const对象可以调用const成员函数
    • const成员函数内不可以调用其它的非const成员函数
    • 非const成员函数内可以调用其它的const成员函
  • 取地址及const取地址操作符重载
  • const补充
    • 场景1
    • 场景2
    • 场景3
    • 场景4

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒 个人主页
🥸🥸🥸 C语言
🐿️🐿️🐿️ C语言例题
🐣🐣🐣 python
🐓🐓🐓 数据结构C语言
🐔🐔🐔 C++
🐿️🐿️🐿️ 文章链接目录

前置++和后置++重载

前置++是先++再使用
后置++是先使用再++
用operator实现前置和后置++感觉非常难
因为operator++只能实现这两个的其中一个功能,为了解决这个问题就需要让operator可以特殊处理
为了让operator++可以进行区分,可以让其中的一个operator++强行增加一个int参数构成重载来区分
注意这里的int是被规定的

所以Date& Date::operator++()表示前置++,Date Date::operator++(int )表示后置++,编译器会自动识别

前置++的实现

因为前置++要求的是先++再使用,所以返回的结果应该是修改完后的对象,返回方式是引用返回,因为可能要支持连续赋值

Date& Date::operator++()代码

//++d
Date& Date::operator++()
{
	*this += 1;
	return *this;
}

在这里插入图片描述

后置++的实现

后置++是先用后++,所以返回的方式不可以是引用返回,因为引用返回是返回的修改后的对象,而我们需要的是返回修改前的对象
所以需要将修改前的对象先拷贝构造出一个局部对象tmp,然后修改再修改this指针,最后再将tmp返回(注意这里tmp是局部对象,出了作用域就会销毁,但是由于我们没有用引用,所以返回的tmp是被拷贝了的,tmp被销毁并不影响)

Date Date::operator++(int )代码

//d++
Date Date::operator++(int )
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}

后置++要想打印返回的结果可以将d1++用括号括起来,因为返回的是一个对象,所以(d1++).Print也是可以打印出结果的
如果不括起来就只能打印修改后的对象
在这里插入图片描述

前置++和后置++也可以显示写的
前置要写成d1. operator()
后置要写成d1. operator(0),注意这里的0可以是任意整形
在这里插入图片描述
前置++和后置++相比,前置++的使用效率会略高一些,因为前置++是通过引用返回,而后置++是传引用返回,传值和传引用的返回效率会有所不同,所以在能使用前置++的情况下我们通常都会使用前置++

前置–和后置–重载

前置–的实现

Date& Date::operator–( )代码

后置–的实现

Date Date::operator–(int )代码

流插入运算符重载

C++中我们并不能通过cout<<d1<<endl去打印对象
而是通过调用Print()函数去打印,但是有了operator后我们就可以实现流插入运算符重载
在这里插入图片描述

流插入运算符重载的实现

	void operator <<(ostream& out)
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}

这里的ostream&表示这个函数接收的是一个输出流对象
这个代码运行后却报错了
在这里插入图片描述
那我们这样写呢?
在这里插入图片描述
在(运算符重载(上))中有提到过
像运算符重载(上)中实现的比较函数如operator<( const Date& y)
d1<d2其实是d1.operator<(d2)
那这样说cout<<d1就应该是cout.operator<<(d1),这显然是不对的,当我们d1.operator<<(cout)时是可以正常运行的,所以最后的结果是我们写反了😂😂😂
应该把cout<<d1写成d1<<cout
在这里插入图片描述
作为成员函数重载,this指针占据第一个参数,所以Date必须是左操作数,而Date必须是左操作数就说明我们不可以让ostream的对象占据左操作数

但是我们就想写成cout<<d1应该怎么改办呢?
对于上面的话只是针对成员函数重载,要想写成cout<<d1,我们只需要不写成成员函数而是写成全局函数就可以了

在这里插入图片描述

然而写成全局函数就出现了一个问题,就是访问的权限,因为之前的函数是成员函数,可以访问私有的成员变量
而现在写成了全局函数之后,就会因为private限制访问,当我们删除了private后再运行依然报错

在这里插入图片描述
这里的找到一个或多个重定义的符号报错的原因是因为void operator <<(ostream& out, const Date& d)函数在头文件中定义,而其他文件包含了Date.h这个头文件,在程序预处理阶段会将Date.h展开,所以需要声明和定义分离
在这里插入图片描述
C++中的流插入是可以支持连续输出的,所以我们还需要再对函数改改,让他的返回值变成一个输出流对象的别名

ostream& operator <<(ostream& out, const Date& d)
{
	cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

虽然代码可以正常运行了,但是我们是通过将私有删除才成功的,而私有删除会导致可以随便修改对象里的成员变量

解决这个问题的方法是我们不删除私有,而是提供一个Getyear Getmonth Getday函数,这样全局函数就可以这些函数去访问了
还有一种方法就是友元声明
友元函数用一个例子解释的话就是:同学A的允许不认识的人拿他的东西,而同学B一开始是不认识同学A的,在通过友元函数后和同学A相互认识了,所以现在同学B可以拿同学A的东西

流提取运算符重载的实现

istream& operator >>(istream& in,  Date& d)
{
	cout << "请依次输入年月日>";
	in >> d._year >> d._month >> d._day;
	return in;
}

istream&是输入流对象
在这里插入图片描述
从流提取中我们也可以理解C语言的scanf为什么需要传地址了,因为我们输入的值需要对变量进行修改,只有找到变量的地址,通过解引用才能够修改变量

日期类的检查函数

对于之前写的日期类函数有一个小问题,就是有的人写的日期会不符合常理
在这里插入图片描述
我们需要有一个检查的函数去告诉我们这个日期是错误的

bool Date::CheckInvalid()
{
	if (_year <= 0||_month<1||_month>12||_day<1||_day>GetMonthDay(_year,_month))
	{
		return false;
	}
	return true;
}
Date::Date(int year,int month,int day)
{
	_year = year;
	_month = month;
	_day = day;
	if (!CheckInvalid())
	{
		cout << "构造日期非法" << endl;
	}	
}

然后将这个函数加入构造函数中,因为所有的对象都是通过构造函数构造出来的,需要保证构造函数不会出错,其次还有流提取函数

istream& operator >>(istream& in,  Date& d)
{
	while (1)
	{
		cout << "请依次输入年月日>";
		in >> d._year >> d._month >> d._day;
		if (!d.CheckInvalid())
		{
			cout << "输入无效日期,请重新输入" << endl;
		}
		else
		break;
	}
	return in; 
}

在这里插入图片描述

const成员函数

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

const对象不可以调用非const成员函数

这是Print函数,当我们用const修饰的对象去调用他时

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

在这里插入图片描述
const修饰对象报错常见的就是权限的放大问题
d1被const修饰后是不可以修改,而Print函数传参时是隐藏了this指针的,也就是说print函数传的参数是d1的地址,将d1的地址穿进去后就有可能会修改d1,所以这是权限放大的原因

要解决问题就需要用const去修饰函数

void Print()const
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

这里的const是修饰this指针指向的内容,注意const修饰的方式是void Print()const,而不是const void Print()或void const Print()

如果对于全局函数就不能像上面修饰的一样了
void operator <<(ostream& out) 不能修饰成 operator <<(ostream& out)const
void operator >>(ostream& in,Date &d) 不能修饰成 void operator >>(ostream& in,Date &d)const

要搞清楚const是想要修饰什么,void Print()const修饰的是隐藏的this指针
operator <<(ostream& out)和 void operator >>(ostream& in,Date &d)根本就没有隐藏的this指针,所以不需要在后面加const
即使想要加const也要这样加 void operator >>(ostream& in,const Date &d)(但是这样是错误的,因为流提前要改变对象,所以不应该加const,这里只是演示该加在哪)

非const对象可以调用const成员函数

非const对象可以调用const成员函数是因为d1没有被const修饰,所以他是可读可写的,而Print函数被const修饰后只要求可以读取数据内容,并不要求修改数据,所以当然可以,可以把这里理解成权限的缩小

在这里插入图片描述
要注意对于只要求读取数据的函数可以加上const修饰,但不用让所以函数都被const修饰,因为如果有的函数要求修改数据,加上const后就会出现权限放大问题

总结
成员函数中如果是一个对成员变量只进行读访问的函数建议加上const修饰,这样被const修饰的对象和没变const修饰的对象都可以使用
如果是对成员变量进行读写访问的函数,不能加上const修饰,否则会出现权限放大问题

const成员函数内不可以调用其它的非const成员函数

这里还是权限放大问题,const修饰的成员函数表示这个函数内部都是不可以修改Date的成员变量的,而如果里面出现了非const的成员函数,就表示里面可以修改成员变量,这显然不行

非const成员函数内可以调用其它的const成员函

这是权限缩小,所以可以
总之const修饰后的对象注意一下权限是否变大就行了

取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容

class Date
{
private:
	int _year;
};
class A
{
public:
	A* operator&()
	{
		return this;
	}
	const A* operator&()const
	{
		return this;
	}
};
int main()
{
	A aa1;
	const A aa2;
	cout << &aa1 << endl;
	cout << &aa2 << endl;
	return 0;
}

A* operator&()和const A* operator&()const是想返回this指针,而返回的this指针有差别
A* operator&()对传入的this指针没有进行const修饰,并且返回的this指针也没有进行const修饰
而const A* operator&()const对传入的this指针进行了修饰,并且返回的this指针也进行了const修饰

通过调用这两个函数得到的this指针可以直接打印出this指针的值
在这里插入图片描述
当我们屏蔽掉上面的代码后,也可以正常运行,因为这是默认成员函数,我们不写编译器会生成,而这两个默认成员函数并不像我们之前写的拷贝函数和析构函数等等默认成员函数那样复杂,这两个默认成员函数就是返回一个this指针,所以日常都不需要我们写,编译器默认生成的函数就够用了
在这里插入图片描述
那什么时候是需要我们写的呢?
比如我们只想让被const修饰后的成员函数拿到地址,这样做的目的就是不想有人拿到地址后乱搞

class Date
{
private:
	int _year;
};
class A
{
public:
	A* operator&()
	{
		return nullptr;
	}
	const A* operator&()const
	{
		return this;
	}
};
int main()
{
	A aa1;
	const A aa2;
	cout << &aa1 << endl;
	cout << &aa2 << endl;
	return 0;
}

在这里插入图片描述
甚至我们还可以返回一个假地址

在这里插入图片描述
在这里插入图片描述

const补充

场景1

int main()
{
	const int i = 0;
	int j = i; 
		cout << j << endl;
	return 0;
}

这里j=i没有报错是因为j是i的拷贝,将i的值拷贝给了j
在这里插入图片描述

场景2

int main()
{
	const int i = 0;
	int& r = i; 
		cout << r << endl;
	return 0;
}

这里的r是i的别名,r被修改会导致i也会被修改,所以报错
在这里插入图片描述

场景3

int main()
{
	int i = 0;
	const int* p1 = &i;
	int* p2 = p1;
	return 0;
}

const修饰p1表示不可以通过p1去修改p1指向的值i,换句话来说就是不可以修改p1
在这里插入图片描述
但是可以修改p1指向的地址,也就是p1可以被修改
在这里插入图片描述
上面的代码中 int
p2 = p1是想将p1指向的地址传给p2,而p1指向的地址是&i
根据前面的结论,const修饰p1表示p1不可以被修改,而p1将i的地址给了p2,p2就可以通过p1给的地址去修改i,也就间接的使*p1修改了,所以才会报错

在这里插入图片描述

场景4

int main()
{
	int i = 0;
	int* const  p1 = &i;
	int* p2 = p1;
	return 0;
}

const修饰p1表示p1不可以被修改,因为p1是一个指针,而p1保存的是i的地址,p1不能被修改意思就是i的地址不可以修改

将p1的值给p2,这里并不会报错,我猜测可能p2也是拷贝了p1的地址,所以p2的改变不会影响p1
在这里插入图片描述
如果想让上面的代码报错,我们可以修改p1,让p1保存另一个变量j的地址,因为const修饰的是p1,p1是不能变的,所以就会报错
在这里插入图片描述

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

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

相关文章

1、css3 动态button展示学习

效果图&#xff1a; 1、首先创建html代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title…

LNMP分布式搭建

一、准备三台主机 192.168.100.11 mysql 192.168.100.12 nginx 192.168.100.13 php 二、关闭防火墙及安全策略 systemctl stop firewalld setenforce 0 三、安装nginx&#xff08;192.168.100.11&#xff09; 1、添加nginx源 vim /etc/yum.repos.d/ng…

数据库系统概念(第七周 第一堂)(E-R模型)

目录 前言 基本概念 观点与模型 作用与要求 E-R模型元素 实体&#xff08;entity&#xff09; 实体集&#xff08;entity set&#xff09; 属性&#xff08;attribute&#xff09; 域&#xff08;domain&#xff09; 码 &#xff08;key&#xff09; 联系 &#x…

allure测试报告用例数和 pytest执行用例数不相同问题

我出现的奇怪问题&#xff1a; pytest执行了9条用例&#xff0c;但是测试报告确只显示3条用例 我将其中的一个代码删除后&#xff0c;发现allure测试报告又正常了 我觉得很奇怪这个代码只是删除了二维数组的第一列&#xff0c;我检查了半天都找不到问题&#xff0c;只有降低版本…

优选算法一:双指针算法与练习(移动0)

目录 双指针算法讲解 移动零 双指针算法讲解 常见的双指针有两种形式&#xff0c;一种是对撞指针&#xff0c;一种是快慢指针。 对撞指针&#xff1a;一般用于顺序结构中&#xff0c;也称左右指针。 对撞指针从两端向中间移动。一个指针从最左端开始&#xff0c;另一个从最…

云计算与 openstack

文章目录 一、 虚拟化二、云计算2.1 IT系统架构的发展2.2 云计算2.3 云计算的服务类型 三、Openstack3.1 OpenStack核心组件 一、 虚拟化 虚拟化使得在一台物理的服务器上可以跑多台虚拟机&#xff0c;虚拟机共享物理机的 CPU、内存、IO 硬件资源&#xff0c;但逻辑上虚拟机之…

Python魔法之旅-魔法方法(04)

目录 一、概述 1、定义 2、作用 二、主要应用场景 1、构造和析构 2、操作符重载 3、字符串和表示 4、容器管理 5、可调用对象 6、上下文管理 7、属性访问和描述符 8、迭代器和生成器 9、数值类型 10、复制和序列化 11、自定义元类行为 12、自定义类行为 13、类…

【香橙派 AIpro】新手保姆级开箱教程:Linux镜像+vscode远程连接

香橙派 AIpro 开发板 AI 应用部署测评 写在最前面一、开发板概述官方资料试用印象适用场景 二、详细开发前准备步骤1. 环境准备2. 环境搭建3. vscode安装ssh插件4. 香橙派 AIpro 添加连接配置5. 连接香橙派 AIpro6. SSH配置 二、详细开发步骤1. 登录 juypter lab2. 样例运行3. …

Windows11 安装Oracle11gR2

一、下载Oracle 11gR2 安装包下载地址&#xff1a;Database Software Downloads | Oracle 下载两个压缩包&#xff0c;下载完成后解压缩到同一个目录。 二、安装Oracle 11gR2 Oracle安装是单程票&#xff0c;因为Oracle卸载特别麻烦&#xff0c;因此最好一次通过。 2.1 安…

排八字软件有哪些?

排八字软件有哪些&#xff1f;在市面上有很多排八字的软件可供选择&#xff0c;其中一些比较知名的有&#xff1a; 无敌八字排盘软件&#xff1a;这是一款功能强大的八字排盘软件&#xff0c;提供详细的八字解析和命理分析服务&#xff0c;且完全免费。 网易星盘&#xff1a;网…

【JAVA |String类】JAVA中的String类常见用法详解

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; &#x1f388;丠丠64-CSDN博客&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起…

500元以内的蓝牙耳机哪个牌子好?首推四大热门品牌盘点

在500元以内的预算范围内&#xff0c;蓝牙耳机试市场上还是有很多可以选择的&#xff0c;它们以出色的音质、舒适的佩戴体验和稳定的连接性能赢得了消费者的青睐&#xff0c;作为一个蓝牙耳机的重度使用者&#xff0c;下也用过不少的500元以内的蓝牙耳机&#xff0c;下面就给大…

小白跟做江科大32单片机之光敏传感器控制蜂鸣器

代码部分 1.思路 通过光敏电阻&#xff0c;控制蜂鸣器的发声 2.butter.h代码 #ifndef _BUTTER__H #define _BUTTER__H void butter_Init(void); void butter_on(void); void butter_off(void); #endif 3.butter.c代码 #include "stm32f10x.h" void butter…

React-组件通信

组件通信 概念&#xff1a;组件通信就是组件之间的数据传递&#xff0c;根据组件嵌套关系的不同&#xff0c;有不同的通信方法 父传子 基础实现 实现步骤&#xff1a; 1.父组件传递数据-在子组件标签上绑定属性 2.子组件接收数据-子组件通过props参数接收数据 props说明 1.…

【C++题解】1446. 人口增长问题

问题&#xff1a;1446. 人口增长问题 类型&#xff1a;循环应用 题目描述&#xff1a; 我国现有 x 亿人口&#xff0c;按照每年 0.1% 的增长速度&#xff0c;n 年后将有多少人&#xff1f; 输入&#xff1a; 一行&#xff0c;包含两个整数 x 和 n &#xff0c;分别是人口基…

Centos 7下的VulFocus靶场搭建详细教程

一、靶场介绍 自带 Flag 功能&#xff1a;每次启动 flag 都会自动更新&#xff0c;明确漏洞是否利用成功。带有计分功能。兼容 Vulhub、Vulapps 中所有漏洞镜像。 二、下载安装 下载 VMware 软件下载 centos镜像 三、Docker知识 学习链接&#xff1a;https://www.runoob.c…

lynis安全漏洞扫描工具

Lynis是一款Unix系统的安全审计以及加固工具&#xff0c;能够进行深层次的安全扫描&#xff0c;其目的是检测潜在的时间并对未来的系统加固提供建议。这款软件会扫描一般系统信息&#xff0c;脆弱软件包以及潜在的错误配置。 安装 方式1 git下载使用git clone https://github…

宏集JMobile Studio—实现HMI界面高自由度设计

一、简介 物联网HMI的组态软件是数据可视化的重要工具&#xff0c;工程师可以通过图形化界面来配置、监控和管理现场采集的数据。目前&#xff0c;市面上大多数的组态软件里的可视化控件库都由设计师预先部署&#xff0c;用户只能调用而不能完全自定义控件&#xff0c;导致可视…

Java时间类--JDK8

为什么JDK8会又新增时间相关类呢&#xff1f; ① JDK7的时间对象如果需要比较大小的话&#xff0c;必须都先转换成毫秒值&#xff1b;JDK8则不需要&#xff0c;可以直接比较。 ② JDK7的时间对象可以修改&#xff0c;在多线程环境下就会导致数据不安全&#xff1b;JDK8不能修改…

【哈希】用哈希桶封装unordered_map unordered_set

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; C进阶 &#x1f389;其它专栏&#xff1a; C初阶 | Linux | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解 用哈希桶封装 unordered_map & unordered_set 的相关内容。 如…