运算符重载(下)

news2024/11/15 19:32:13

目录

  • 前置++和后置++重载
    • 前置++的实现
      • 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是不能变的,所以就会报错
在这里插入图片描述

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

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

相关文章

完蛋,AI一不小心把手机影像颠覆了

文&#xff5c;刘俊宏 2024上半年的发售季&#xff0c;手机影像大战再起。 近年来&#xff0c;影像年年卷&#xff0c;年年挤牙膏&#xff0c;直到AI大模型来临。 刚推出的荣耀跟法国百年摄影工作室雅顾合作&#xff0c;主打手机上拍出2万一张人像照片的高级感。华为nou在Pu…

【Python】 如何从列表中移除第一个元素?

基本原理 在Python中&#xff0c;列表是一种非常灵活的数据结构&#xff0c;可以存储一系列的元素。这些元素可以是任何类型&#xff0c;包括数字、字符串、其他列表等。列表中的元素是有序的&#xff0c;并且可以通过索引来访问和修改。 当我们想要从列表中移除第一个元素时…

【C++】哈希(2万字)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 unordered系列关联式容器 unordered_map unordered_map的文档介绍 unordered_map的接口说明 unordered_set 底层结构 哈希概念 哈希冲突 哈希函数 哈希…

Linux CFS调度器之周期性调度器scheduler_tick函数

文章目录 前言一、简介二、源码分析2.1 scheduler_tick2.2 task_tick2.3 entity_tick2.4 check_preempt_tick2.5 resched_curr 参考资料 前言 Linux内核调度器主要是主调度器和周期性调度器&#xff0c;主调度器请参考&#xff1a;Linux 进程调度之schdule主调度器 一、简介 …

【JavaEE】多线程(1)

&#x1f386;&#x1f386;&#x1f386;个人主页&#x1f386;&#x1f386;&#x1f386; &#x1f386;&#x1f386;&#x1f386;JavaEE专栏&#x1f386;&#x1f386;&#x1f386; &#x1f386;&#x1f386;&#x1f386;计算机是怎么工作的&#x1f386;&#x1f3…

ctfshow web刷题

1.红包题第六弹 作者给了提示 F12查看页面源代码 <html lang"zh-CN"> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8" /> <meta name"viewport" content"widthdevice-…

低代码赋能企业数字化转型:数百家软件公司的成功实践

本文转载于葡萄城公众号&#xff0c;原文链接&#xff1a;https://mp.weixin.qq.com/s/gN8Rq9TDmkMpCtNMMsBUXQ 导读 在当今的软件开发时代&#xff0c;以新技术助力企业数字化转型已经成为一个热门话题。如何快速适应技术变革&#xff0c;构建符合时代需求的技术能力和业务模…

Python网页处理与爬虫实战:使用Requests库进行网页数据抓取

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

【C++修行之道】类和对象(三)拷贝构造函数

目录 一、 概念 二、特征 正确的拷贝构造函数写法&#xff1a; 拷贝函数的另一种写法 三、若未显式定义&#xff0c;编译器会生成默认的拷贝构造函数。 四、编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了&#xff0c;还需要自己显式实现吗&#xff1f; 深拷…

Linux系统安全及其应用

文章目录 一、用户账号安全管理1.1 系统账号的清理1.2 对用户账号的操作1.2.1 锁定和解锁用户1.2.2 删除无用账号 1.3 对重要文件进行锁定1.4 密码安全控制1.4.1 新建用户1.4.2 已有用户 二、历史命令管理2.1 历史命令限制2.2 自动清空历史命令 三、设置终端登录的安全管理3.1 …

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

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

Kotlin 2.0 重磅发布! 性能提升!新功能上线!开发者必看!

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

2.7HDR与LDR

一、基本概念 1.基本概念 动态范围&#xff08;Dynamic Range&#xff09; 最高亮度 / 最低亮度 HDR High Dynamic RangeLDR Low Dynamic Range HDR与LDR和Tonemapping的对应关系&#xff1a; 我们常用的各种显示器屏幕&#xff0c;由于不同的厂家不同的工艺导致它们的…

【GlobalMapper精品教程】083:基于DEM整体抬升或下降地形高程的两种方式

在Globalmapper24中文版中,可以很方便地对DEM高程数据进行整体抬升或下降。 文章目录 一、准备DEM数据二、高程整体修改1. 修改偏移2. 数据导出三、栅格计算器一、准备DEM数据 订阅专栏后,从私信查收实验数据及Globalmapper24中文版,加载实验数据包data083.rar中的dem数据,…

Unity【入门】脚本基础

Unity脚本基础 文章目录 1、脚本基本规则1、创建规则2、MonoBehavior基类3、不继承MonoBehavior的类4、执行的先后顺序5、默认脚本内容 2、生命周期函数1、概念2、生命周期函数有哪些3、生命周期函数支持继承多态 3、Inspector窗口可编辑的变量4、Mono中的重要内容1、重要成员2…

一个良好的嵌入式系统框架(基于FreeRTOS)

目录 Unix操作系统里的优先级嵌入式系统里的优先级 Unix操作系统里的优先级 在《Unix传奇》中有这样一句话&#xff0c;用户态的进程/线程是三等公民、root线程是二等公民、硬件中断是一等公民。 在操作系统中&#xff0c;"用户态"和"内核态"是两种不同的…

深入Kafka消息分区机制:从原理到实践

深入Kafka消息分区机制&#xff1a;从原理到实践 在现代分布式系统中&#xff0c;如何高效地处理海量数据是一个至关重要的问题。Apache Kafka作为一种高吞吐量的分布式消息系统&#xff0c;广泛应用于日志收集、实时分析等场景。为了保证数据的高效处理和系统的高可扩展性&am…

全新/二手KEITHLEY 2400 数字万用表

吉时利Keithley 2400数字源表&#xff0c;200V&#xff0c;1A&#xff0c;20W Keithley 2400 源表是一款 20W 仪器&#xff0c;可输出和测量 5V&#xff08;输出&#xff09;和 1V&#xff08;测量&#xff09;至 200V DC 的电压以及 10pA 至 1A 的电流。该万用表功能包括高重复…

2024 RCTF WebMisc部分 WP

Misc gogogo 考点:内存取证 得到 gogogo.raw 内存取证的题用volatility和AXIOM结合分析 AXIOM 分析存在云服务 但是百度网盘要密码 https://pan.baidu.com/share/init?surlZllFd8IK-oHvTCYl61_7Kw 发现访问过sqlite数据库 可以尝试提取数据库文件出来 结合 volatility 第…

Flutter基础 -- Dart 语言 -- 列表集合枚举

目录 1. 列表 List 1.1 初始 1.2 声明 1.2.1 自动 1.2.2 定长 1.2.3 生成数据 1.3 属性 1.4 方法 1.4.1 添加 1.4.2 查询 1.4.3 删除 1.4.4 Range 1.4.5 洗牌 1.4.6 排序 1.4.7 复制子列表 1.4.8 操作符 2. 集合 Map 2.1 初始 2.2 声明 2.2.1 松散 2.2.2 …