Let’s Make C++ Great Again——运算符重载练手程序

代码

Date.h

#pragma once
#include <iostream>
using namespace std;
class Date
{
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
public:
	Date(int _year = 0, int _month = 0, int _day = 0); // 建议将默认参数放到声明中(不能声明和定义都放,会造成重定义默认参数),放到定义中可能会导致错误"没有合适的默认构造函数可用"
	void print();

	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=(const Date& d);
	


	Date& operator+=(int d);
	Date operator+(int d);
	Date& operator-=(int d);
	Date operator-(int d);

	Date& operator++();
	Date operator++(int);

	Date& operator--();
	Date operator--(int);


private:
	int is_leap(int _month);
	int getDays(int _year, int _month);
	int _year;
	int _month;
	int _day;
};

Date.cpp

#include "Date.h"

Date::Date(int _year, int _month, int _day) // 在声明中设置了参数默认值,定义就不能设置了,否则报出“重定义缺省参数”
{
	this->_year = _year;
	this->_month = _month;
	this->_day = _day;
}
void Date::print()
{
	cout << this->_year << '-' << this->_month << '-' << this->_day << endl;
}
int Date::is_leap(int _year)
{
	if (_year % 4 == 0 && _year % 100 != 0 || _year % 400 == 0)
	{
		return 1;
	}
	return 0;
}
int Date::getDays(int _year, int _month)
{
	int months[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	return (_month == 2 ? months[_month] + is_leap(_year) : months[_month]);
}

bool Date::operator<(const Date& d)
{
	if (d._year != this->_year)
	{
		return this->_year < d._year;
	}
	if (d._month != this->_month)
	{
		return this->_month < d._month;
	}
	if (d._day != this->_day)
	{
		return this->_day < d._day;
	}
	return false; // 补充代码
}
bool Date::operator<=(const Date& d)
{
	/*if (d._year != this->_year)
	{
		return this->_year < d._year;
	}
	if (d._month != this->_month)
	{
		return this->_month < d._month;
	}
	if (d._day != this->_day)
	{
		return this->_day < d._day;
	}
	return true;*/

	return !(*this > d);
}
bool Date::operator>(const Date& d)
{
	if (d._year != this->_year)
	{
		return this->_year > d._year;
	}
	if (d._month != this->_month)
	{
		return this->_month > d._month;
	}
	if (d._day != this->_day)
	{
		return this->_day > d._day;
	}
	return false; // 补充代码
}
bool Date::operator>=(const Date& d)
{
	/*if (d._year != this->_year)
	{
		return this->_year < d._year;
	}
	if (d._month != this->_month)
	{
		return this->_month < d._month;
	}
	if (d._day != this->_day)
	{
		return this->_day < d._day;
	}
	return true;*/

	return !(*this < d);
}
bool Date::operator==(const Date& d)
{
	return d._year == this->_year
		&& d._month == this->_month
		&& d._day == this->_day;
}
bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}



Date& Date::operator=(const Date& d)
{
	if (*this != d)
	{
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
	}
	return *this;
}

Date& Date::operator+=(int d) // 如果是我难以使用到这么简便的代码
{
	if (d < 0) // 针对d为负数的情况
	{
		return (*this -= -d);
	}
	this->_day += d;
	while (this->_day >= getDays(this->_year, this->_month))
	{
		this->_day -= getDays(this->_year, this->_month);
		this->_month++;
		if (this->_month > 12)
		{
			this->_month = 1;
			this->_year++;
		}
	}
	return *this;
}
Date Date::operator+(int d) // 加法不应该影响调用它的对象,需要创建一个临时变量,所以返回值类型不能是引用
{
	if (d < 0)
	{
		return (*this - -d);
	}
	Date tmp(*this);
	tmp._day += d;
	while (tmp._day >= getDays(tmp._year, tmp._month))
	{
		tmp._day -= getDays(tmp._year, tmp._month);
		tmp._month++;
		if (tmp._month > 12)
		{
			tmp._month = 1;
			tmp._year++;
		}
	}
	return tmp;
}
Date& Date::operator-=(int d)
{
	if (d < 0) // 针对d为负数的情况
	{
		return (*this += -d);
	}
	this->_day -= d;
	//while (this->_day <= 0)
	//{
	//	this->_day += getDays(this->_year, --this->_month); // 不能和+=相同
	//	if (this->_month == 0)
	//	{
	//		this->_month = 12;
	//		this->_year--;
	//	}
	//}
	while (this->_day <= 0)
	{
		if (--this->_month == 0)
		{
			this->_month = 12;
			this->_year--;
		}
		this->_day += getDays(this->_year, this->_month);
	}
	return *this;
}
Date Date::operator-(int d)
{
	if (d < 0) // 针对d为负数的情况
	{
		return (*this + -d);
	}
	Date tmp(*this);
	tmp._day -= d;
	while (tmp._day <= 0)
	{
		if (--tmp._month == 0)
		{
			tmp._month = 12;
			tmp._year--;
		}
		tmp._day += getDays(tmp._year, tmp._month);
	}
	return tmp;
}

Date& Date::operator++()
{
	return (*this += 1);
}
Date Date::operator++(int)
{
	Date tmp(*this);
	++(*this);
	return tmp;
}

Date& Date::operator--()
{
	return (*this -= 1);
}
Date Date::operator--(int)
{
	Date tmp(*this);
	--(*this);
	return tmp;
}

test.cpp

#include "Date.h"
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << '-' << d._month << '-' << d._day;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}
int main()
{
	Date d1;
	cin >> d1; // 2022 1 1
	Date d2 = d1;
	Date d3;
	d2 = d3 = d1;
	d1 -= 10;
	cout << d1 << endl;
	d1--;
	cout << d1 << endl;
	d1++;
	cout << d1 << endl;
	d1 += 10;
	cout << d1 << endl;
	d1 -= -100;
	cout << d1 << endl;
	d1 = d1 - -100;
	cout << d1 << endl;
	d1 += -100;
	cout << d1 << endl;
	d1 = d1 + -100;
	cout << d1 << endl;
	cout << (d1 > d2) << endl;
	cout << (d1 >= d2) << endl;
	cout << (d1 <= d2) << endl;
	cout << (d1 < d2) << endl;
	cout << (d1 == d2) << endl;
	cout << (d1 != d2) << endl;
	d3 = d1 + 10;
	cout << d3 << endl;
	d3 = d1 - 10;
	cout << d3 << endl;
	return 0;
}

运行结果

在这里插入图片描述

分析

  • 对于构造函数中的缺省参数的设置:如果是声明与定义分开写的话,缺省参数只能设置在声明中,否则,报出重定义缺省参数

  • print函数其实可以被流插入运算符重载替换。
    流插入和流提取运算符重载,实现成全局函数更合适一些。
    因为实现成成员变量,函数参数列表处this会默认排在第一个,导致只有对象 << cout;对象 >> cin;时,才能正常进行输入输出,导致操作别扭。

    但是,将这两个函数设置成成员变量,也有个阻碍就是,全局函数不像成员函数一样,无法访问对象的私有或保护成员变量或函数。此时,将这两个运算符重载函数设置成友元函数即可

  • 返回值引用使用在了:=重载、+=重载、-=重载、前置++重载、后置–重载、流插入重载、流提取重载;

    原因:

    1. 允许链式操作。(尤其对于=、+=、-=)
      a = b = c实际情况是:a = (b = c)b = c的返回结果是b
      如果返回临时对象,a = b操作开始前,该临时对象的生命周期已经结束。无法完成链式赋值操作。但是我在vs2022中为value返回值时,可以正常链式赋值,可能时编译器进行了优化吧(留有疑问)。
    2. 返回引用,不需要调用构造函数和析构函数。提升了效率。
  • 在实现函数重载时,其实可以提高代码的复用。
    比如==就是!(!=)>=就是!<++就是+= 1等等诸如此类。

  • +=、-=、-、+运算符重载时的代码陷阱
    因为默认这些操作时的操作数都是正数,忽略了操作数为负数的情况。导致代码逻辑出现了bug。

    解决:使用if判断,让负数跳转到合适的函数中即可。

  • 赋值运算符重载时,防止自我赋值。
    防止自我赋值的意义是很大的,首先,为了效率毋庸置疑。其次,如果成员变量中存在属性在堆中,在进行赋值前,一定先进行地是,释放堆中资源,防止内存泄漏,此时进行自我赋值,则会导致数据丢失。

  • 将只在成员函数中使用的两个成员函数(is_leap()``getDays())的权限设置成私有,为了更好的封装性。

  • 前置++、-- 和 后置++、–
    实际上,运算符重载时的函数名只有:operator运算符这一种形式。那么如何对前置和后置运算符进行区分呢?C++设计者使用了整型占位符进行区分:即在后置运算符的形参列表中加入int
    前置运算符返回值可以是引用(返回的是操作数本身),而后置运算符返回值不可以是引用(返回的是重载函数中的局部变量)。这和前置后置操作的逻辑有关。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FeatherWaves

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值