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、创建项目:首先,在进度猫中创建新的项目,并设置项目的时间、工作日等参数。根据…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
