[Operator Overloading] Date Class Actual Combat

This article has participated in the "Newcomer Creation Ceremony" event to start the road of gold creation together.

1. Task list


  • Size comparison of two date objects: < , >, =, ==, >=, <=
  • Date object plus or minus days: ++ (pre and post), -- (pre and post), +=, -=, +, -
  • Number of days between two date objects:-
  • Overloads for Stream Extraction and Stream Insertion

2. Basic principles


  • ① It must be correct to return by value , that is, copying is required to increase additional overhead. Whether or not the reference can be returned depends on whether the referenced object is still there after the function is exited
  • ② Implement the reuse between functions as much as possible
  • ③ It is recommended to use the form of inline function for functions with short length. Note that the definition and declaration of an inline function cannot be separated, otherwise the inline function will not be added to the symbol table during compilation, resulting in a link error. So it's better to write it into the class, because the functions in the class are inline by default, and you can easily access the member variables
  • ④ When reusing functions, the number of copies should be reduced as much as possible to improve efficiency

3. Function realization


① Constructor

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

private:
	int _year;
	int _month;
	int _day;
};

②The size of the two objects

 We only implement the overloading of > and ==, and the others can be reused ( all are written in the class without special instructions, in order to reduce the space, the class will not be written out)

bool operator== (const Date& d) const
{
	return _year == d._year && _month == d._month && _day == d._day;
}


bool operator> (const Date& d) const
{
	if (_year > d._year ||
		_year == d._year && _month > d._month ||
		_year == d._year && _month == d._month && _day > d._day) 
		return true;
	else
		return false;
}

【comments】

  • ① Use reference by value to reduce copying, preferably with const protection
  • ② When the member variables of the object do not need to be modified, it is best to add const protection to the this pointer, so that both the pass object and the const object can call the member function . Reference article
bool operator>= (const Date& d) const
{
	return operator>(d) || operator==(d);
}

bool operator< (const Date& d) const
{
	return !operator>=(d);
}

bool operator <= (const Date& d) const
{
	return !operator>(d);
}

③ Date object plus or minus days

1. Overloading of addition and subtraction operators
int Getmonthday(int year, int month) const // 之后反复用到,不再赘述
{
	static int day[13] = {0, 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 29;
	else
		return day[month];
}


Date operator+ (const int day) const
{
	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 == 13)
		{
			tmp._month = 1;
			tmp._year++;
		}
	}
	return tmp;
}
Date operator- (const int day) const
{
	Date tmp(*this);
	if (day < 0)
	{
		tmp = tmp + (-day);
		return tmp;
	}
	tmp._day -= day;
	while (tmp._day < 0)
	{
		tmp._month--;
		if (tmp._month == 0)
		{
			tmp._month = 12;
			tmp._year--;
		}
		tmp._day += Getmonthday(tmp._year, tmp._month);
	}
	return tmp;
}

【comments】

  • When performing addition and subtraction operations, it is unchanged, so you need to copy the temporary object first, and finally return the temporary object
  • Because the temporary object will be destroyed after it goes out of scope, it cannot be returned by reference
  • The above code is copied twice each - creating a temporary variable and returning a temporary object
2. Overloading of the += -= operator
Date& operator+= (const int day)
{
	_day += day;
	while (_day > Getmonthday(_year, _month))
	{
		_day -= Getmonthday(_year, _month);
		_month++;
		if (_month == 13)
		{
			_month = 1;
			_year++;
		}
	}
	return *this;
}

Date& operator-= (int day)
{
	if (day < 0)
	{
		*this = *this + (-day);
		return *this;
	}
	_day -= day;
	while (_day < 0)
	{
		_month--;
		if (_month == 0)
		{
			_month = 12;
			_year--;
		}
		_day += Getmonthday(_year, _month);
	}
	return *this;
}

【comments】

  • The basic idea is very similar to the implementation of +-
  • 区别在于+=、-=对象本身是会改变的,所以不需要额外的拷贝,直接在原对象上操作
  • 出函数后原对象并没有被销毁,所以可以使用引用返回减少拷贝
  • +=、 -=的实现过程中没有用到拷贝构造
3.+- 与+= -=之间的复用
// +复用+=
Date operator+ (int day)
{
	Date tmp(*this);
	tmp += day;
	return tmp;
}

// +=复用+
Date& operator += (int day)
{
	*this = *this + day;
	return *this;
}

根据上面代码的比较我们可以得出以下结论:

  • +复用+=的时候,+的重载需要拷贝2次,而+=的重载本身不需要拷贝
  • +=复用+的时候,+的重载需要拷贝2次,而+=的重载也需要拷贝两次
  • 所以综上+复用+=的效果最佳
4.前置++与后置加加
// 前置减减后置减减同理,就不再这里赘述了
Date& operator++ ()    // 前置加加
{
	*this += 1;
	return *this;
}

Date operator++ (int)   // 后置加加
{
	Date tmp(*this);
	*this += 1;
	return tmp;
}

【评注】

  • 为了区别前置加加和后置加加,规定后置加加带一个int型参数,前置加加不带参。注意带的参数类型必须是int类型,形参名可写可不写。
  • 前置加加先加加后使用,所以将自增后的对象本身返回;后置加加先使用后加加,所以将自增前的对象本身返回

④两个对象之间的日期差

int operator- (const Date& d) const
{
	Date max = *this;
	Date min = d;
	if (max < min)
	{
		max = d;
		min = *this;
	}
	int cnt = 0;
	while (max != min)
	{
		min++;
		cnt++;
	}
	return cnt;
}

【评注】

  • 计算两个天数差的方法有很多,这是最简单的方法
  • 还可以计算两个到同一天的时间然后作差 + 1

⑤流插入与流提取运算符的重载

【问题一】为什么C++中的 cin 和 cout 会自动识别类型? 【答】因为流提取运算符和流插入运算符将所有常见的内置类型都给重载了 image-20220707083514762


【问题二】cout 和 cin 是什么? 【答】cout 是 ostream 的全局对象;cin 是 istream 的全局对象

image-20220707083903909

【问题三】流插入运算符和流提取运算符的重载可以写在类里面吗? 【答】不可以。因为类里面的函数第一次参数默认为 this 指针,所以在使用的时候不是 cout << xxx,而变成了xxx << cout 在这里插入图片描述


【问题四】写在函数外面如何访问成员变量 【答】使用友元函数,注意在类里面声明的时候前面需要加上 const,但是在定义的时候不需要加上 const。image-20220707085311574 image-20220707085709605 友元函数的特性:

  • 友元函数可访问类的私有和保护成员,但==不是==类的成员函数,因此也没有this指针
  • 友元函数不能用const修饰(因为没有this指针)
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制

【问题五】定义与声明分离的时候 【答】要在头文件声明,源文件定义,否则会出现链接错误。而类里面定义的函数默认是内联的,不会添加到符号表中,就不会出现问题。类里面长的函数也具有这样内联的属性

Guess you like

Origin juejin.im/post/7118271975658618917