C++(运算符重载+赋值拷贝函数+日期类的书写)
目录
- 运算符重载
- 运算赋值重载+=和+
- 运算赋重载前置++和后置++
- <,<=,>,>=,==,!=运算符重载
- 日期类的实现
- <<流插入和>>流提取的运算符重载
- 总结

运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
注意:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型参数
- 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
- 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this- .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出
现。
先定义一个日期类 (先用日期类作为用例)
定义一个日期类
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
//private:int _year;int _month;int _day;
};
我们先将日期类的成员改成共有的
写一个运算符重载函数operator==
bool operator==(const Date& d1, const Date& d2)
{return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day;
}
可以看见如果写成全局的函数,我们必须将成员变量改成共有的,所以我们可以将运算符重载函数写成成员函数
代码展示
bool operator==(const Date& d2){return _year == d2._year;&& _month == d2._month&& _day == d2._day;}
上面说完运算符重载接下来来讨论一下赋值拷贝函数
由于赋值操作我们改变的是调用这个函数的对象,所以我们在参数中可以加上cosnt
修饰,传值和传引用,我们选择传引用,最后返回也返回引用,这样可以避免调用拷贝构造函数
注意:返回值是*this
代码展示
class Date
{
public :
//构造函数Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
//拷贝构造函数Date (const Date& d){_year = d._year;_month = d._month;_day = d._day;}
//赋值拷贝函数Date& operator=(const Date& d){if(this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}
private:int _year ;int _month ;int _day ;
};
注意:赋值拷贝函数和拷贝构造函数类似,但是调用的场景是不相同的
注意:拷贝构造函数和赋值拷贝函数的调用方式十分相同,但是拷贝构造函数调用是在对象不存在时,在创建的时候,调用拷贝构造函数,赋值拷贝函数是,对象已经存在了,调用的赋值拷贝函数
. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注
意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符
重载完成赋值。
既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实
现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}
private:DataType *_array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 = s1;return 0;
}
注意:赋值拷贝函数和拷贝构造函数一样,当没有malloc和new还有其他动态申请的空间时,是不需要写的,一旦有动态申请的资源存在时,就必须写一个赋值拷贝函数
下图是对上面代码的解释
运算赋值重载+=和+
由于日期类设计到平年和闰年每一个月的日期不同,所以我们可以将1到12月的每个月的天数存在一个数组中,然后获取每个月的天数,这里我们可以只存放平年的,然后闰年的天数只有二月是不相同的,所以二月我们单独拿出来讨论
注意:这个函数我们可以写在类中,类中完成的函数默认都是内联函数,因为我们后面会经常调用这个函数
inline int GetMonthDay(int year, int month){assert(month > 0 && month <= 12);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 monthDayArray[2] + 1;}return monthDayArray[month];}
注意:先判断是否是二月可以增加效率,因为二月只有一个月,如果每次都判断年的话,每次都要进行多余的判断,如果先判断是否是二月的话,如果是二月才进行后面的判断,如果不是二月直接就跳出了
下面来完成运算符重载中的+和+=,注意:我们可以只写一个+=然后用+去复用+=
Date& Date::operator+=(int day)
{if (day < 0){return *this -= (-day);}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month > 12){_month = 1;_year++;}}return *this;
}
//d1+10
Date Date::operator+(int day)
{Date tmp = *this;tmp += day;return tmp;//出作用域会销毁,所以不能返回引用
}
运算赋重载前置++和后置++
由于运算符重载中,运算符只能写在operator的后面,所以我们只能利用函数重载来区别后置++和前置++,前置++可以直接不给参数,后置++可以在参数中给一个int和前置++作区分。
接下来来写一下日期类的后置++和前置++,注意++就是相当于+=1,所以我们可以服用前面的+=运算符重载
后面写的函数全都是成员函数
//前置++
Date& Date::operator++()
{*this += 1;return *this;
}
//为了区分,构成重载,给后置++,强行增加了一个int形参
//这里不需要写形参名,因为接收值是多少不重要,也不需要用
//这个参数仅仅只是一个标志
//后置++
Date& Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}
上面我们写了++操作,–操作也同理
–操作
注意:–操作我们应该先写-=操作和-操作,然后去复用这两个函数
Date& Date::operator-=(int day)
{if (*this < 0){return *this += (-day);}_day -= day;while (_day <= 0){//借上个月的时间_day += GetMonthDay(_year, _month - 1);_month--;if (_month == 0){_year--;_month = 12;}}return *this;
}
Date Date::operator-(int day)
{Date tmp = *this;tmp -= day;return tmp;
}
//前置--
Date& Date::operator--()
{*this -= 1;return *this;
}
//前置--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}
<,<=,>,>=,==,!=运算符重载
注意:对于日期类的比较,我们可以直接比较年,如果年大则返回true
,如果年相当则比较月,如果月大则返回true
,如果月相当则比较日,如果日大,则返回true
,否则剩下的情况则返回false
。
代码展示
bool Date::operator<(const Date& d)//内联函数
{if (_year < d._day){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){if (_day < d._day){return true;}}}return false;
}
bool Date::operator==(const Date& d)
{return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;
}
bool Date::operator>(const Date& d)
{return !(*this <= d);
}
bool Date::operator>=(const Date& d)
{return *this > d || *this == d;
}
bool Date::operator!=(const Date& d)
{return !(*this == d);
}
**我们只用写一个,剩下的全都可以复用
日期类的实现
Date.h
#pragma once
#include<iostream>
#include<cassert>
using namespace std;class Date
{//友元函数声明friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1);void Print() const;//内联不能声明和定义声明,直接写在类中的默认就是内联函数inline int GetMonthDay(int year, int month){assert(month > 0 && month <= 12);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 monthDayArray[2] + 1;}return monthDayArray[month];}bool CheckDate();bool operator<(const Date& d);bool operator<=(const Date& d);bool operator>(const Date& d);bool operator>=(const Date& d);bool operator==(const Date& d);bool operator!=(const Date& d);Date& operator+=(int day);Date operator+(int day);Date& operator-=(int day);Date operator-(int day);//++d1;Date& operator++();//d1++//为了区分,构成重载,给后置++,强行增加了一个int形参Date& operator++(int);////--d1Date& operator--();//d1--Date operator--(int);int operator-(const Date& d);int Getyear(){return _year;}int Getmonth(){return _month;}int Getday(){return _day;}//流插入// 不建议,因为Date*this占据了第一个参数位置,使用d<<cout不符合使用习惯//void operator<<(ostream& out);//第一个参数是隐含的this
private:int _year;int _month;int _day;
};
Date.cpp
#include"Date.h"bool Date::CheckDate()
{if (_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 (!CheckDate()){cout << "日期错误";}
}
void Date::Print() const //加上这个const之后,this指针本身不能改,this指针指向的内容也不能改
{cout << _year << "-" << _month << "-" << _day << endl;
}
//d1<d2
bool Date::operator<(const Date& d)//内联函数
{if (_year < d._day){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){if (_day < d._day){return true;}}}return false;
}
bool Date::operator==(const Date& d)
{return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;
}
bool Date::operator>(const Date& d)
{return !(*this <= d);
}
bool Date::operator>=(const Date& d)
{return *this > d || *this == d;
}
bool Date::operator!=(const Date& d)
{return !(*this == d);
}
//+复用+=
//思路:先加上天上 天满了先加到月上 月满了加到年上
//d1+=10;
Date& Date::operator+=(int day)
{if (day < 0){return *this -= (-day);}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month > 12){_month = 1;_year++;}}return *this;
}
//d1+10
Date Date::operator+(int day)
{Date tmp = *this;tmp += day;return tmp;//出作用域会销毁,所以不能返回引用
}//+=复用+
//Date Date::operator+(int day)
//{
// Date tmp = *this;
// tmp._day += day;
// while (tmp._day > GetMonthDay(tmp._year, tmp._month))
// {
// tmp._day -= GetMonthDay(tmp._year, tmp._month);
// tmp._month++;
// if (tmp._month > 12)
// {
// tmp._month = 1;
// tmp._year++;
// }
// }
// return tmp;
//}
//
//Date& Date::operator+=(int day)
//{
// *this = *this + day;
// return *this;
//}
Date& Date::operator-=(int day)
{if (*this < 0){return *this += (-day);}_day -= day;while (_day <= 0){//借上个月的时间_day += GetMonthDay(_year, _month - 1);_month--;if (_month == 0){_year--;_month = 12;}}return *this;
}
Date Date::operator-(int day)
{Date tmp = *this;tmp -= day;return tmp;
}
Date& Date::operator++()
{*this += 1;return *this;
}
//为了区分,构成重载,给后置++,强行增加了一个int形参
//这里不需要写形参名,因为接收值是多少不重要,也不需要用
//这个参数仅仅
Date& Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}
//前置--
Date& Date::operator--()
{*this -= 1;return *this;
}
//前置--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}
int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int flag = 1;if (*this < d){//赋值拷贝max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;//flag控制的是符号,如果前面大就是正的,如果后面大就是负数
}
<<流插入和>>流提取的运算符重载
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}
istream& operator>>(istream& in, Date& d)
{cout << "请依次输入年月日:";in >> d._year >> d._month >> d._day;return in;
}
这里加入了返回值之后,就可以进行连续的流插入和流提取了
总结
在本文中,我们深入探讨了运算符重载和赋值拷贝函数在C++中的应用。通过运算符重载,我们可以为自定义类型定义各种操作,使得代码更加清晰和易读。而赋值拷贝函数则在对象拷贝和赋值过程中起到了至关重要的作用,确保对象之间的正确复制和管理。通过深入理解和熟练应用这些概念,我们可以写出更加健壮和高效的代码。
在实践中,我们需要注意运算符重载和赋值拷贝函数的使用场景和规范,以避免潜在的错误和性能问题。同时,对于特定的项目和需求,我们也可以进一步扩展和定制这些功能,以满足更复杂的应用场景。
最后,我希望本文能够帮助读者更好地理解和应用运算符重载和赋值拷贝函数,并在实际开发中发挥出它们的作用。让我们继续探索C++语言的奥秘,写出更加优雅和强大的代码!
相关文章:

C++(运算符重载+赋值拷贝函数+日期类的书写)
目录 运算符重载运算赋值重载和运算赋重载前置和后置<,<,>,>,,!运算符重载日期类的实现<<流插入和>>流提取的运算符重载总结 运算符重载 C为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回…...

【介绍下负载均衡原理及算法】
🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…...

CESS 受邀出席香港Web3.0标准化协会第一次理事会议,共商行业未来
2024 年 4 月 5 日,CESS(Cumulus Encrypted Storage System)作为香港 Web3.0 标准化协会的副理事会成员,于香港出席了 2024 年度第一次理事会会议。此次会议汇聚了来自不同领域的知名企业和专家(参会代表名单见文末&am…...

MySQL 8.0.19安装教程(windows 64位)
在c盘目录下的Program Files目录下创建MySQL目录,将下载好的mysql解压到里面 解压完是这个样子 配置初始化的my.ini文件的文件 [mysqld] # 设置3306端口 port3306 # 设置mysql的安装目录 basedirC:\Program Files\MySQL # 设置mysql数据库的数据的存放目录 datad…...

探索AI提示词网站:助力内容创作与AI对话
嗨,大家好!在这个充满创意的时代里,AI技术为我们带来了许多惊喜和便利。如果你是一个内容创作者,无论是在撰写博客还是进行科技对话,今天我将向大家介绍几个能够提升与AI对话效率的神奇网站。 1. FlowGPT 首先…...

AdaBoost 算法
目录 什么是 AdaBoost 算法? Adaboost 的 7 个优缺点 集成学习:人多力量大: Bagging:民主。Boosting :挑选精英。长短期记忆网络:引入遗忘机制 生成对抗网络 :物竞天择适者生存 首先,了解一下集成学习及 Boosting 算法 集成学习归属于机器学习,他是一种「训练思路…...

链接分析算法
链接分析(Link Analysis)通常指的是对图(Graph)中的节点(Nodes)和边(Edges)进行分析,以发现图的结构和属性。在图论中,链接分析算法通常用于解决诸如网页排名…...

怎么批量完成图片格式转换?介绍三种简单方法
在日常生活和工作中,我们经常会遇到需要将图片格式转换的情况,无论是为了适应不同的设备要求,还是为了能让我们的图片应用到更多的使用场景中去,批量图片格式转换都是一项非常实用的技能。本文将介绍一些常见的批量图片格式转换方…...

每日OJ题_BFS解决最短路③_力扣127. 单词接龙
目录 ③力扣127. 单词接龙 解析代码 ③力扣127. 单词接龙 127. 单词接龙 难度 困难 字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk: 每一对相邻的单词只差一个字母。…...

微信小程序英文版:实现一键切换中英双语版(已组件化)
已经重新优化代码做成了组件,需要可自取:https://github.com/CrystalCAI11/wechat-language-compoment 所有操作都打包在组件里不需要在额外的地方添加代码,直接在你需要的页面里导入组件,再在对应页面的onLoad()里set文本就行了。…...

openstack之neutron介绍
核心组件 neutron-server:提供API接口,把对应的api请求传给plugin进; neutron-plugin:管理逻辑网络状态,调用agent; neutron-agent:在provider network上创建网络对象; neutron-…...

学习Rust的第三天:猜谜游戏
基于Steve Klabnik的《The Rust Programming Language》一书。今天我们在rust中建立一个猜谜游戏。 Introduction 介绍 We will build a game that will pick a random number between 1 to 100 and the user has to guess the number on a correct guess the user wins. 我们将…...

React中子传父的方式及原理
方式挺多的,先说最常用的通过props进行父子组件的数据传递和修改以及原理 在React中,props不仅用于传递数据,它们也可以传递可以执行的函数,这使得子组件能够间接更新父组件的状态。这种方法强化了React的单向数据流策略…...

【数据结构与算法】贪心算法及例题
目录 贪心算法例题一:找零问题例题二:走廊搬运物品最优方案问题输入样例例题三:贪心自助餐 贪心算法 贪心算法是一种在每一步选择中都采取当前状态下最优的选择,以期望最终达到全局最优解的算法。它的核心思想是每次都选择当前最…...

【Origin+Python】使用External Python批量出图代码参考
目录 基本介绍环境配置官方代码示例基础代码详解我的代码效果视频进阶代码及去水印 基本介绍 origin2021后可以使用python实现批量绘图,一共有两种方式:一种是嵌入式Python,一种是外部Python访问Origin。详细介绍可以自己去查看,打…...

YOLOv8最新改进系列:融合DySample超轻量动态上采样算子,低延迟、高性能,目前最新上采样方法!!!遥遥领先!
YOLOv8最新改进系列:融合DySample超轻量动态上采样算子,低延迟、高性能,目前最新上采样方法!!!遥遥领先! DySample超轻量动态上采样算子全文戳这!here! 详细的改进教程以及源码&am…...

ChatGPT基础(二) ChatGPT的使用和调优
文章目录 ChatGPT的特性采用关键词进行提问给ChatGPT指定身份提升问答质量的策略1.表述方式上的优化2.用"继续"输出长内容3.营造场景4.由浅入深,提升问题质量5.预设回答框架和风格 ChatGPT的特性 1.能够联系上下文进行回答 ChatGPT回答问题是有上下文的&…...

麒麟 V10 离线 安装 k8s 和kuboard
目录 安装文件准备 主机准备 主机配置 修改主机名(三个节点分别执行) 配置hosts(所有节点) 关闭防火墙、selinux、swap、dnsmasq(所有节点) 安装依赖包(所有节点) 系统参数设置(所有节点) 时间同步…...

PlayerSettings.WebGL.emscriptenArgs设置无效的问题
1)PlayerSettings.WebGL.emscriptenArgs设置无效的问题 2)多个小资源包合并为大资源包的疑问 3)AssetBundle在移动设备上丢失 4)Unity云渲染插件RenderStreaming,如何实现多用户分别有独立的操作 这是第381篇UWA技术知…...

项目管理工具——使用甘特图制定项目计划的详细步骤
甘特图是一种直观的项目管理工具,它有助于我们清晰地展示任务安排、时间管理和项目的进度。以下是使用甘特图制定项目计划的详细步骤: 1、创建项目:首先,在进度猫中创建新的项目,并设置项目的时间、工作日等参数。根据…...

python读取文件数据写入到数据库中,并反向从数据库读取保存到本地
学python,操作数据库是必不可少的,不光要会写python代码,还要会写SQL语句,本篇文章主要讲如何把本地txt文件中的数据读取出来并写入到对应的数据库中,同时将数据库单个表中的数据读出来保存在本地txt文件中。 话不多说…...

社交媒体数据恢复:Viber
Viber是一款流行的即时通讯应用,用于发送消息、语音通话和视频通话。然而,有时候我们会不小心删除一些重要的Viber聊天记录,这时候就需要进行数据恢复。本文将介绍如何在安卓设备上进行Viber数据恢复。 一、使用安卓数据恢复软件 安卓数据恢…...

蓝桥杯赛事介绍
蓝桥杯是由工业和信息化部人才交流中心主办的全国性IT学科赛事,全称为“蓝桥杯全国软件和信息技术专业人才大赛”。该赛事旨在推动软件和信息领域专业技术人才培养,提升大学生的创新能力和就业竞争力,为行业输送具有创新能力和实践能力的高端…...

TypeScript系列之-深度理解基本类型画图讲解
JS的类型(8): null undefined string number boolean bigint symbol object(含 Array, Function,Date.....) TS的类型(87): 以上所有,加上 void, never, enum, unknown, any 再加上自定义类型 type interface 上一节我们说…...

Debian
使用root用户操作 直接使用su命令进行切换。 配置用户使用sudo命令 在安装好系统之后,使用用户名登录之后。需要执行需要root权限的命令,会发现无法执行成功。原因是没有配置用户使用sudo的权限。 编辑bash /etc/sudoers文件 可以先切换root用户安装…...

怎么使用JMeter进行性能测试?
一、简介 JMeter是Apache软件基金会下的一款开源的性能测试工具,完全由Java开发。它专注于对我们应用程序进行负载测试和性能测量,最初设计用于web应用程序,现在已经扩展到其他测试功能,比如:FTP、Database和LDAP等。…...

MySQL:锁的分类
文章目录 行级锁Record LockGap LockNext-Key Lock插入意向锁 表级锁表锁元数据锁(MDL)意向锁AUTO-INC 锁 全局锁 行级锁 Record Lock 记录锁有S锁(共享锁/读锁)和X锁(排他锁/写锁)之分,加完S…...

基于springboot实现房屋租赁管理系统设计项目【项目源码+论文说明】
基于springboot实现房屋租赁管理系统设计演示 摘要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对房屋租赁信息管理混乱&…...

揭秘Redis底层:一窥数据结构的奥秘与魅力
一、引言 Redis,以其高性能、高可靠、丰富的数据结构等特点,成为现代应用程序中不可或缺的缓存与存储组件。然而,Redis之所以能够实现如此卓越的性能,离不开其底层精巧的数据结构设计。本文将深入浅出地解析Redis底层五大核心数据…...

【网站项目】智能停车场管理系统小程序
🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板ÿ…...