C++(运算符重载+赋值拷贝函数+日期类的书写)

目录

  • 运算符重载
  • 运算赋值重载+=和+
  • 运算赋重载前置++和后置++
  • <,<=,>,>=,==,!=运算符重载
  • 日期类的实现
  • <<流插入和>>流提取的运算符重载
  • 总结

在这里插入图片描述

运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。

注意:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
    藏的this
  5. .* :: 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++语言的奥秘,写出更加优雅和强大的代码!

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

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

相关文章

(最新)华为 2024 届实习招聘-硬件通⽤/单板开发——第十一套和十二套

&#xff08;最新&#xff09;华为 2024 届实习招聘-硬件通⽤/单板开发——第十一套和十二套 部分题目分享&#xff0c;完整版带答案(有答案和解析&#xff0c;答案非官方&#xff0c;未仔细校正&#xff0c;仅供参考&#xff09;&#xff08;共十套&#xff09;获取&#xff…

【软考】设计模式之命令模式

目录 1. 说明2. 应用场景3. 结构图4. 构成5. 优缺点5.1 优点5.2 缺点 6. 适用性7.java示例 1. 说明 1.命令模式&#xff08;Command Pattern&#xff09;是一种数据驱动的设计模式。2.属于行为型模式。3.请求以命令的形式被封装在对象中&#xff0c;并传递给调用对象。4.调用对…

面试经典150题——买卖股票的最佳时机

面试经典150题 day7 题目来源我的题解方法一 暴力 &#xff08;超时&#xff09;方法二 动态规划方法三 动态规划 空间优化方法四 一次遍历 题目来源 力扣每日一题&#xff1b;题序&#xff1a;121 我的题解 方法一 暴力 &#xff08;超时&#xff09; 求每一对&#xff08;…

Kalman滤波器的原理与实现

Kalman滤波器是一种用于估计系统状态的算法,其可以通过融合系统的动态模型和传感器测量值来提高状态估计的精度。其原理基于线性系统理论和最优估计准则,主要用于处理具有高斯噪声的线性系统。 以下是Kalman滤波器的基本原理和实现步骤: 系统动态模型:首先需要建立系统的动…

十八章 hive基础

1&#xff09; 表的分区 大数据开发数据量较大&#xff0c;在进行数据查询计算时&#xff0c;需要对数据进行拆分&#xff0c;提升查询速度。 1-1 单个分区 单个分区时创建单个目录 1-2 多个分区 多个分区可以将数据拆分多个目录储存 注意点&#xff1a; 1 分组字段不能…

Win10 启动时数字键盘不亮怎么办?

首先&#xff0c;按住winr 运行 windows 运行框&#xff0c;输入regedit 打开注册表编辑器 1.查找current_user选项下的Control Panel 中的keyboard 点击八initialkeyboard 值设置成2&#xff0c;如图所示即可。2.选择hkey_user 中的。default 下的control Panel 选项下的keyb…

mysql断电后无法启动

1、配置/etc/my.cnf innodb_force_recovery6(这个值越小越安全,最开始建议设置为1&#xff0c;如果也无法启动再改为2&#xff0c;以此内推)1 (SRV_FORCE_IGNORE_CORRUPT)&#xff1a;即使服务器检测到损坏的页仍让它运行。试图使SELECT* FROM tbl_name跳过损坏的索引记录和页…

JavaScript事件循环

JavaScript是单线程&#xff0c;&#xff08;通常用来操作DOM&#xff0c;如果改为多线程&#xff0c;一个线程进行了1删除&#xff0c;另一个添加了DOM&#xff0c;那么浏览器该怎么处理&#xff09; 在JavaScript中&#xff0c;所有任务都可分为 同步任务&#xff1a;立即执…

美易官方:道指六连阴!美国股债双杀背后

美国股市和债市近期遭遇了罕见的双重打击&#xff0c;道指连续六个交易日下跌&#xff0c;引发市场广泛关注。这一现象背后&#xff0c;是否又上演了某一“魔咒”&#xff1f;本文将从多个角度对此进行深入分析。 道指罕见创下六连阴、标普500指数迎来硅谷银行危机以来最大两日…

工作中用Redis最多的10种场景

作者&#xff1a;苏三说技术 链接&#xff1a;https://juejin.cn/post/7325132133168971813 前言 Redis是一种非常优秀的基于KV的键值对缓存数据库&#xff0c;有非常不错的性能和稳定性&#xff0c;无论是在工作中&#xff0c;还是在面试中&#xff0c;都经常会出现。因此&am…

村庄规划中基础地理数据的获取、处理与应用

文章目录 一、三调数据1. 地类图斑2. 村级调查区二、调查数据1. 宅基地确权数据2. 公益林数据三、公开数据1. 卫星影像2. 数字高程模型3. 国家地理信息服务平台4. 其他数据一、三调数据 三调数据,是对现状土地利用情况的全面调查。成果数据以数据库的形式存储、组织与管理。具…

UC网盘、迅雷网盘拉新如何做?热门拉新项目盘点

迅雷网盘拉新项目&#xff1a; ​关键词链接 双重拉新模式&#xff1a;可以通过链接、关键词两种方式进行推广&#xff0c;匹配不同拉新场景。 网盘功能齐全&#xff1a;作为老牌网盘产品&#xff0c;功能强大&#xff0c;使用体验好&#xff0c;支持不同格式文件。 不限推广…

【七 (2)FineBI FCP模拟试卷-平台新增用户留存分析】

目录 文章导航一、字段解释1、用户平台登录信息表格2、用户平台激活信息表格 二、需求三、操作步骤1、建立用户平台登录信息表格和用户平台激活信息表格的关联关系2、将用户平台激活信息表格的激活日期添加到用户平台登录信息表格3、新增公式列&#xff0c;计算激活时间和登录时…

OpenHarmony轻量系统开发【5】驱动之GPIO点灯

5.1点灯例程源码 先看最简单得LED灯闪烁操作 源码结构如下&#xff1a; 第一个BUILD.gn文件内容&#xff1a; static_library("led_demo") {sources ["led_demo.c"]include_dirs ["//utils/native/lite/include","//kernel/liteos_m/c…

工业物联网让“制造”变成“智造”!——青创智通

工业物联网解决方案-工业IOT-青创智通 随着科技的不断进步和工业的持续发展&#xff0c;物联网&#xff08;IoT&#xff09;技术的出现为制造业带来了前所未有的变革。工业物联网&#xff08;IIoT&#xff09;作为物联网技术在工业领域的应用&#xff0c;正在逐渐改变传统的制…

@Transactional使用中的三类坑

我们知道事务有声明式事务和编程式事务两种&#xff0c;编程式事务代码侵入较高&#xff0c;声明式事务侵入较低&#xff0c;在项目中常有使用&#xff0c;然而&#xff0c;不正确的使用声明式事务&#xff0c;可能让代码未能按照我们的预期执行。 一、事务可能没有生效 Tran…

概念:Android 虚拟机

Android中的Dalvik虚拟机&#xff08;DVM&#xff09;和Android运行时&#xff08;ART&#xff09;都是运行在内存中的。它们是Android操作系统的一部分&#xff0c;为Android应用提供了一个管理执行代码的环境。具体来说&#xff1a; Dalvik虚拟机 (DVM)&#xff1a;Dalvik是…

2024-4.python4基本数据类型

基本数据类型 引言 提问 前面我们说过&#xff0c;计算机的本质作用就是用来存储和运算二进制的数据。但是在实际应用中&#xff0c;我们看到计算机存储或者运算的数据并非只有二进制的数据&#xff0c;例如使用Excel可以对一些数值数据、文本数据或者图像数据进行不同形式的处…

从iPhone恢复已删除照片的最佳软件

本文分享了从iPhone恢复已删除照片的最佳软件。如果您正在寻找如何从iPhone恢复已删除的照片&#xff0c;请查看这篇文章。 为什么您需要软件从iPhone恢复已删除的照片&#xff1f; 没有什么比丢失iPhone上的重要数据更痛苦的了&#xff0c;尤其是一些具有珍贵回忆的照片。有时…

从零开始学习深度强化学习及其在自动驾驶下的实战项目

深度强化学习及其在自动驾驶下的实战项目 本课程是为了帮助大家快速入门强化学习和学会应用深度强化学习进行算法的开发。 鉴于网上有许多开源的强化学习理论基础的视频&#xff0c;而且许多项目都是基于gym游戏进行开发的简单项目&#xff0c;并未涉及使用前沿的carla环境等自…