栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

目录

代码

Date.hDate.cpptest.cpp运行结果 分析

代码 Date.h
#pragma once
#include 
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)
{
	

	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)
{
	

	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;时,才能正常进行输入输出,导致操作别扭。

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

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

原因:

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

在实现函数重载时,其实可以提高代码的复用。
比如==就是!(!=),>=就是!<,++就是+= 1等等诸如此类。

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

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

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

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

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/778991.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号