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、创建项目:首先,在进度猫中创建新的项目,并设置项目的时间、工作日等参数。根据…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
