C++ Classes and Objects (4)—Implementation of Date Class

Table of contents

1. Class creation and method declaration

2. Output & operator overloading

3. Check the legality

1. Get the number of days corresponding to the year and month

2. Initialization

4. Implement addition and addition operations

1. Write += first and then + 

2. Write + first and then +=

3. Comparison of two methods

5. Realize ++ self-increase and -- self-decrement

1. Self-increment

2. Self-decreasing

6. Implement subtraction and subtraction operations

1. Decrease the number of waiting days

2. Add negative numbers and subtract positive numbers

3. Reduce the number of days

4. Date minus date

full version:

Date.h

Date.cpp

Test.cpp


Through previous learning, this time we useDate class to practice what we have learned. Let’s go firstReview.

 

If we don't write the default member function, the compiler will automatically generate it. If we write it, the compiler will not automatically generate it.

Constructors and destructors are generated by default:

  • The built-in type construction is not processed, and the destructor cleans it up.
  • Custom types will call corresponding construction/destruction

Copy construction and assignment overloading are generated by default

  • Built-in types complete shallow copy/value copy (copy by byte one by one).
  • Custom type, to call this member copy construction/assignment overloading 

1. Class creation and method declaration

We create declarations of member variables (properties) and member functions (methods) of the date class in the header file, and the definitions are placed in the source file.

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;

class Date {
public:
	Date(int year = 0, int month = 0, int day = 0);

	int GetMonthDay(int year, int month);

    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+=(int day);
	Date operator+(int day);
	
	Date& operator-=(int day);

	// d1 - 100
	Date operator-(int day);

	// d1 - d2;
	int operator-(const Date& d);

	// ++d1
	Date& operator++();

	// d1++
	Date operator++(int);
	
	Date& operator--();

	Date operator--(int);

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

2. Output & operator overloading

For detailed explanation, please see:Operator overloading

void Date::Print()
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

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

// d1 < d2
bool Date::operator<(const Date& d)
{
	return _year < d._year
		|| (_year == d._year && _month < d._month)
		|| (_year == d._year && _month == d._month && _day < d._day);
}

// d1 <= d2
bool Date::operator<=(const Date& d)
{
	return *this < d || *this == d;
}

// d1 > d2
bool Date::operator>(const Date& d)
{
	return !(*this <= d);
}

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

bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

3. Check the legality

First, we need to check the validity of the date. The month is in the range of 1-12. We get the number of days corresponding to the year and month through GetMonthDay, and check the date in the constructor used for initialization.

1. Get the number of days corresponding to the year and month

int Date::GetMonthDay(int year, int month)
{
	assert(month > 0 && month < 13);
	int _month[12] = { 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 _month[month - 1];
	}
}
  • First, use assert to determine whether the parameter month is legal. If it is not legal, an error will be reported.
  • Then create an integer array _month that stores the number of days in the corresponding month.
  • If the month passed in is February and the year is a leap year, 29 days will be returned. Otherwise, the number of days in the corresponding month in the _month array will be returned.

2. Initialization

Date::Date(int year, int month, int day)
{
	if (month > 0 && month < 13
		&& (day > 0 && day <= GetMonthDay(year, month)))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "日期非法" << endl;
	}
}
  • First check if the month is legal and the number of days is legal, then initialize.
  • Otherwise, "Illegal date" is output to prompt an initialization error.

4. Implement addition and addition operations

1. Write += first and then + 

How to calculate date plus number of days?

Date d1(2023, 2, 4);
Date d2 = d1 + 100;

Idea:

  • If the number of days in the current date is added, if it is greater than the number of days in the current month, the month will be rounded up. When the month is greater than December, the year will be rounded up, and the added result *this will be returned. However, this method will change d1, so this method actually implements +=.
Date& Date::operator+=(int day)
{
    _day += day;
    while (_day>GetMonthDay(_year,_month))
    {
        _day -= GetMonthDay(_year,_month);
        month++;
        if(month == 13)
        {
            _year++;
            _month = 1;
        }
    }
    return *this;
}

+= also needs to return a value. += supports continuous +=, but the date class does not support addition and other types. In the following situation, += must have a return value.

Date d3 = d1;
d1 = d3 += 100;

Date operations such as d1=d3+=100 can be supported. At this time, the return value of the operation needs to be added. From the characteristics of the operator, the return value is required.

We implement addition operations by reusing addition and other operations.

Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp += day;
	return tmp;
}
  • Create a date class object tmp to temporarily save *this,
  • Perform addition and other operations on tmp,
  • Finally, tmp is returned (using temporary variables can ensure that the original variables are not changed, which is consistent with the definition of + operation)

In addition, tmp is a temporary object and disappears when it goes out of scope, so it cannot be returned by reference.

2. Write + first and then +=

The second method can implement the + operation first, and then implement the += operation by reusing the + operation.

Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp._day += day;
	while (tmp._day > GetMonthDay(_year, _month))
	{
		tmp._day -= GetMonthDay(_year, _month);
		tmp._month++;
		if (tmp._month == 13)
		{
			tmp._year++;
			tmp._month = 1;
		}
	}
	return tmp;
}
Date& Date::operator+=(int day)
{
	*this = *this + day;
	return *this;
}
  • In the + operation, the temporary variable tmp is stored as the changed value of *this, the + operation is performed on the member day of tmp, and finally tmp is returned.
  • Use *this in the += operation to perform + operations on the object calling the function, and finally return *this.​ 

If you use static to modify tmp in the + operation to make it a static variable and a global life cycle, can it be returned by reference?

//Date.cpp
Date& Date::operator+(int day)
{
	static Date tmp(*this);
	tmp._day += day;
	while (tmp._day > GetMonthDay(_year, _month))
	{
		tmp._day -= GetMonthDay(_year, _month);
		tmp._month++;
		if (tmp._month == 13)
		{
			tmp._year++;
			tmp._month = 1;
		}
	}
	return tmp;
}
//test.cpp
void TestDate2()
{
	Date d1(2023, 2, 4);
	d1.Print();

	Date d2 = d1 + 100;
	d2.Print();

	Date d3 = d1 + 100;
	d3.Print();
}


int main()
{
	TestDate2();
	return 0;
}

 We can see that the result of d2 is correct, but the result of d3 is incorrect. d3 should add 100 to d1, but the actual output result is to add 100 to d2.

Next let's debug and take a look:

When running to Date d3 = d1 + 100;, before tmp is initialized inside the + operation, why are the member values ​​of tmp different from the member values ​​of *this?

Reason: Static variables can only be initialized once. Date d2 = d1 + 100; is initialized the first time it is used. The second time it is initialized it will have no effect. Therefore, static variables should be used with caution. You cannot use static to modify tmp for passing by reference. .

3. Comparison of two methods

The first is to write the add and so on operations first, and reuse the add and so on to implement the add operations.

The second is to write the addition operation first and then reuse the addition to implement the addition and other operations.

So which of the above two methods is better?

Conclusion: The first one is better, but the second + operation is expensive. Initializing tmp requires a copy construction, and returning by value also requires a copy construction; while adding and so on has no copy behavior.

If you add, wait, reuse and add, it will increase the number of times the copy structure is used twice. Writing add first, adding, etc., reusing and adding will consume a total of four copies. If you write the add and wait function first, there will be no copy constructor. Only add, reuse, add and wait will use two copy constructors.

From such a comparison, it can be concluded that the first method is better to write the addition first and then the addition, which can reduce the number of copies.

Reduction constitutes operator overloading and function overloading

5. Realize ++ self-increase and -- self-decrement

1. Self-increment

The increment operator ++ is divided into prefix ++ and postfix ++.

By default, there is only prefix ++. At the same time, ++ has only one operand, which is the hidden this pointer. The value of *this is returned, and you can use reference return.

//++d
Date& Date::operator++()
{
	*this += 1;
	return *this;
}

How to distinguish between pre- and post-processing?

The compiler has done special processing to make it easier to identify that a parameter int is added to the suffix. It has no practical meaning. It is just to distinguish the function overload. The suffix needs to return the value before ++, so the temporary variable tmp is used to save *this. to *this++, then returns the unchanged *this saved by tmp.

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

Use prefix whenever you can, which can reduce copy operations.​ 

Use the post-parameter to pass a parameter, any one is enough. This parameter is just for placeholder, to distinguish it from the pre-parameter overload.

Let’s test it out: 

void TestDate3()
{
	Date d1(2023, 6, 6);
	d1.Print();

	Date ret1 = ++d1;
	d1.Print();
	ret1.Print();

	Date ret2 = d1++;
	d1.Print();
	ret2.Print();
}
int main()
{
	TestDate3();
	return 0;
}

Output result: 

2. Self-decreasing

In the same way, self-decrement is also implemented in the same way as self-increment.

//--d
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}

// d-- 
Date Date::operator--(int)
{
	Date tmp(*this);
	*this -= 1;

	return tmp;
}

6. Implement subtraction and subtraction operations

1. Decrease the number of waiting days

Do subtraction on the date. If the subtraction is less than or equal to zero, borrow the date from the previous month. If the date is borrowed in January, borrow the date from December of the previous year.

Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			--_year;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
void TestDate4()
{
	Date d1(2023, 2, 4);
	d1.Print();

	d1 -= 100;
	d1.Print();
}

int main()
{
	TestDate4();
	return 0;
}

Output result: 

 

2. Add negative numbers and subtract positive numbers

What if the following situation occurs?

void TestDate4()
{
	Date d2(2023, 6, 6);
	d2 += -100;

	Date d3(2023, 7, 7);
	d3 -= -200;
}

Add judgment statements for addition and subtraction. If the addition is a negative number, the subtraction is called, and if the integer is subtracted and a negative number is subtracted, the addition is called.

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

Let’s test it again 

void TestDate4()
{
	Date d2(2023, 6, 6);
	d2.Print();
	d2 += -100;
	d2.Print();

	Date d3(2023, 7, 7);
	d3.Print();
	d3 -= -200;
	d3.Print();
}

int main()
{
	TestDate4();
	return 0;
}

 Output result:

 

3. Reduce the number of days

It is implemented by reusing operations such as subtraction and using the temporary variable tmp.​ 

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

4. Date minus date

This function calculates the difference in days by incrementing the smaller date object until the two date objects are equal. If the current date object is greater than the passed in date object, the result is a positive number; if the current date object is smaller than the passed in date object, the result is a negative number.

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;
}
  • First, create two temporary date objects max and min, initialized to the current date object and the incoming date object d respectively.
  • If the current date object is smaller than the passed in date object d, max and min are swapped, and the flag flag is set to -1, indicating that the result is a negative number.
  • Create an integer variable n to record the absolute value of the difference in days.
  • Using a loop, when min is not equal to max, do the following: increment min's date, i.e. execute min.operator++(). Also increment n.
  • Returns n multiplied by flag to get the final difference in days.

have a test

void TestDate5()
{
	Date d1(2024, 1, 12);
	d1.Print();

	Date d2(2023, 11, 23);
	d2.Print();

	cout << d2 - d1 << endl;
	cout << d1 - d2 << endl;
}
int main()
{
	TestDate5();
	return 0;
}

Successful implementation:

full version:

Date.h

#include <iostream>
#include <assert.h>
using namespace std;

class Date {
public:
	Date(int year = 0, int month = 0, int day = 0);
	void Print();
	int GetMonthDay(int year, int month);

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

	// d1 - 100
	Date operator-(int day);

	// d1 - d2;
	int operator-(const Date& d);

	// ++d1
	Date& operator++();

	// d1++
	Date operator++(int);
	
	Date& operator--();

	Date operator--(int);

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

Date.cpp

#include "Date.h"

int Date::GetMonthDay(int year, int month)
{
	assert(month > 0 && month < 13);
	int _month[12] = { 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 _month[month - 1];
	}
}

Date::Date(int year, int month, int day)
{
	if (month > 0 && month < 13
		&& (day > 0 && day <= GetMonthDay(year, month)))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout << "日期非法" << endl;
	}
}

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

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

// d-- 
Date Date::operator--(int)
{
	Date tmp(*this);
	*this -= 1;

	return tmp;
}

Date& Date::operator-=(int day)
{
	if (day < 0)
	{
		*this += -day;
		return *this;
	}
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			--_year;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
Date Date::operator-(int day)
{
	Date tmp(*this);
	tmp -= day;
	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;
}

void Date::Print()
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

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

// d1 < d2
bool Date::operator<(const Date& d)
{
	return _year < d._year
		|| (_year == d._year && _month < d._month)
		|| (_year == d._year && _month == d._month && _day < d._day);
}

// d1 <= d2
bool Date::operator<=(const Date& d)
{
	return *this < d || *this == d;
}

// d1 > d2
bool Date::operator>(const Date& d)
{
	return !(*this <= d);
}

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

bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

Test.cpp

#include "Date.h"

void TestDate1()
{
	Date d1(2023, 2, 4);
	cout << d1.GetMonthDay(2022,12) << endl;
	//d1.Print();

	//Date d2 = d1 + 100;
	//d2.Print();
}

void TestDate2()
{
	Date d1(2023, 2, 4);
	d1.Print();

	Date d2 = d1 + 100;
	d2.Print();

	Date d3 = d1 + 100;
	d3.Print();
}

void TestDate3()
{
	Date d1(2023, 6, 6);
	d1.Print();

	Date ret1 = ++d1;  // d1.operator++();
	d1.Print();
	ret1.Print();

	Date ret2 = d1++;  // d1.operator++(0);
	d1.Print();
	ret2.Print();


	/*d1.operator++();
	d1.operator++(0);*/
}

void TestDate4()
{
	//Date d1(2023, 2, 4);
	//d1.Print();

	//d1 -= 100;
	//d1.Print();

	Date d2(2023, 6, 6);
	d2.Print();
	d2 += -100;
	d2.Print();

	Date d3(2023, 7, 7);
	d3.Print();
	d3 -= -200;
	d3.Print();
}
void TestDate5()
{
	Date d1(2024, 1, 12);
	d1.Print();

	Date d2(2023, 11, 23);
	d2.Print();

	cout << d2 - d1 << endl;
	cout << d1 - d2 << endl;
}
int main()
{
	TestDate5();
	return 0;
}

Guess you like

Origin blog.csdn.net/m0_73800602/article/details/134581207