C++ classes and objects (3) Operator overloading, const member functions, date class implementation

Preface

        This article mainly introduces the knowledge of operator overloading and const member functions. Operator overloading is an important tool for designing classes. Among them, assignment operator overloading and address operator overloading are the default member functions of the class. Finally, a comparison is implemented based on this article and previous knowledge about classes and objects. Complete date class.

The examples in this article are mainly based on date class demonstration:

// 日期类
class Date
{
public:
	// 参数全缺省的构造函数
	Date(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 拷贝构造函数
	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}

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

Table of contents

Preface

1 Operator overloading

introduce

Overloaded assignment operator

Get address reload

Demonstration of other operator overloading

Overloading +-, +=, -=

Prefix++,--

Postfix++,--

Comparison: >, <, >=, <=, ==, !=

Overloaded output operator<<

Overloaded input operators >>

Edit

overload[ ]

2 const member functions

3 Implement date class


1 Operator overloading

introduce

        An overloaded operator is a function with a special name, which is composed of the keyword operator plus the operator symbol. Operator overloading enables operators to be extended to custom types of operations, greatly increasing the readability of the code.

Notice:

  • New operators cannot be created, that is, operator can only overload existing operators.
  • Overloaded operators must have a parameter of class type.
  • Operators of built-in types cannot be redefined.

Note the operators in the following table that cannot be overloaded: :: .* . ? :

Operator overloading example, overloading date type == operator:

// 重载成全局函数
bool operator==(const Date d1, const Date d2)
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}

We can overload the global == for comparison of date class objects, but then we need to set the member variables of the date class to public, or enable the function to access private members through friends (explained later). In order to achieve encapsulation, this article directly overloads the operator into a member function of the class, paying attention to the this pointer implicit in the function parameter.

// 在类中声明 bool operator == (const Date d);

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

Date d1, d2;
d1 == d2; // 调用运算符函数
d1.operator==(d2); // 显式调用

Overloaded assignment operator

        The assignment operator is a function called operator= . A class can control how its objects are assigned values ​​through the assignment operator. Like the copy constructor, if the assignment operator is not defined in the class, the compiler will synthesize one.

        The assignment operator can only be overloaded as a member function of the class and cannot be overloaded as a global function.

Reason: If we do not explicitly define the assignment operator in the class, the compiler will generate a default assignment operator, which will conflict with the global assignment operator. Therefore, the assignment operator can only be overloaded into a member function of the class. .

        As a binary operator, the object on the left side of the assignment operator is naturally bound to the this parameter, and the object on the right side is a parameter of the same type . To comply with continuous assignment, the return type of the assignment operator should be *this . Parameters and return types are generally references to improve efficiency.

Overload the assignment operator of the date class:

// 赋值重载
Date& Date::operator=(const Date& d)
{
	if (this != &d) // 检查是否给自己赋值
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	return *this;
}
int main()
{
	Date d1;
	Date d2(2023, 9, 1);
	d1.Print();

	d1 = d2;   // d1.operator(d2)
	d1.Print();
	return 0;
}

operation result:

The assignment operator overload generated by the compiler by default is similar to the default copy constructor, copying by value byte by byte. Built-in type member variables are directly assigned values, while custom type members are assigned values ​​by calling its assignment operator . For example:

class Time
{
public:
	Time()
	{
		_hour = 12;
		_minute = 0;
		_second = 0;
	}

	Time& operator=(const Time& t)
	{
		if (this != &t)
		{
			_hour = t._hour;
			_minute = t._minute;
			_second = t._second;
		}

		return *this;
	}

private:
	int _hour;
	int _minute;
	int _second;
};

class DateTime
{
public:
	DateTime(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
	// 自定义类型成员
	Time _t;
};

int main()
{
	DateTime d1;
	DateTime d2;

	d1 = d2;// 调用默认的赋值运算符函数
	// 内置类型直接赋值
	// d1._year = d2._year;
	// d1._month = d2._month;
	// d1._day = d2._day;

	// d1._t = d2._t; // 调用Time类型的赋值运算符函数
	return 0;
}

For date classes, we can use the default assignment operator function. When the class involves resource management, we need to implement the assignment operator of the class ourselves. For example, the stack class can refer to its copy constructor to implement assignment operator overloading.

Get address reload

        Address overloading and const ( see below for const member functions ) address overloading is the default member function of the class. Generally, we do not need to define it, and it is generated by the compiler by default.

// 日期类的默认取地址重载
Date* operator&()
{
	return this;
}

const Date* operator&() const
{
	return this;
}

Demonstration of other operator overloading

First define a GetMonthDay function to facilitate date carry:

int Date::GetMonthDay(int year, int month)
{
	static int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

	if (month == 2 && ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)))
		return 29;

	return MonthDay[month];
}

Overloading +-, +=, -=

// 日期+天数
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 == 13)
		{
			_year++;
			_month = 1;
		}
	}

	return *this;
}

Date Date::operator+(int day) 
{
	Date tmp(*this);
	tmp += day;  // 复用+=

	return tmp;
}

// 日期-天数
Date& Date::operator-=(int day)
{
	if (day < 0)
		return *this += (-day);

	_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;
}

Prefix++,--

// 日期+1天
Date& Date::operator++()
{
	*this += 1;  // 复用+=
	return *this;// 返回++后的日期
}
// 日期-1天
Date& Date::operator--()
{
	*this -= 1;  // 复用-=
	return *this;// 返回--后的日期
}

Postfix++,--

In order to distinguish it from the prefix ++ and --, C++ stipulates that the postfix ++ and -- must add an int formal parameter. When explicitly calling the postfix ++, -- an int value (usually 0) must be passed.

Date Date::operator++(int)
{
	Date tmp(*this);

	*this += 1;
	
	return tmp; // 返回++前的值
}

Date Date::operator--(int)
{
	Date tmp(*this);

	*this -= 1;
 
	return tmp; // 返回--前的值
}

Date d1;
d1++;            // 后置++
d1.operator++(0);// 显式调用

Comparison: >, <, >=, <=, ==, !=

bool Date::operator>(const Date& d) 
{
	if (_year > d._year)
		return true;
	else if (_year == d._year && _month > d._month)
		return true;
	else if (_year == d._year && _month == d._month && _day > d._day)
		return true;
	else
		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); // 复用>
}

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

Overloaded output operator<<

        The IO standard library uses >> and << to perform input and output operations. The library has overloaded the built-in type version as shown below. We can overload the >> and << of the class to implement input and output of class objects.

        The first formal parameter of the output operator is a reference to the non-constant object of ostream. The second formal parameter is usually a constant reference, because printing the object usually does not change its content. In order to support continuous output, the return type is a formal parameter of ostream.

For example, overloading << for date class:

// 在类中声明友元函数	
friend ostream& operator<<(ostream& out, const Date& d);

// 重载日期类的全局<<
ostream& operator<<(ostream& cout, const Date& d)
{
	cout << d._year << "年" << d._month << "月" << d._day << "日";

	return out;
}

At this time, the member function Print does not need to exist.

Input and output operators must be non-member functions . If the input and output operators are member functions of a class, then the objects on the left side of them can only be objects of the class. This form is not compatible with the standard library.

Date d;

d << cout; // If operator<< is a member function of the class

Overloaded input operators >>

        The first parameter of the input operator is the introduction of the stream (iostream object, etc.) to be read, and the second parameter is the reference to the (non-const) object to be read. The return value is usually a reference to a stream.

For example, overloading the date class >>:

// 类中声明友元函数
friend istream& operator>>(istream& in, Date& d);

// 重载日期类的全局>>
istream& operator>>(istream& cin, Date& d)
{
	in >> d._year >> d._month >> d._day;

	return in;
}

Input and output test:

int main()
{
	Date d1(2023,1,1);

	cout << d1 << endl;

	cout << "输入日期:";
	cin >> d1;

	cout << d1 << endl;

	return 0;
}

operation result:

overload[ ]

When implementing a sequence table in C language, printing the elements of the sequence table can only be achieved by defining a Print function. Now simply design a sequence table, and overload the [ ] operator to make accessing the sequence table data as convenient and intuitive as an array.

#include<assert.h>
// 顺序表
struct SeqList
{
public:
	void PushBack(const int& x)
	{
		// ... 扩容
		_a[_size++] = x;
	}

	size_t size() 
	{
		return _size;
	}

	// 读/写
	int& operator[](size_t i)
	{
		assert(i < _size);

		return _a[i];
	}

private:
	// C++11
	int* _a = (int*)malloc(sizeof(int) * 10);
	size_t _size = 0;
	size_t _capacity = 0;
};

int main()
{
	SeqList sl;
	sl.PushBack(1);
	sl.PushBack(2);
	sl.PushBack(3);
	sl.PushBack(4);

	for (size_t i = 0; i < sl.size(); i++)
	{
		sl[i] *= 2; // 修改
		cout << sl[i] << " "; // 读取
	}

	return 0;
}

operation result:

2 const member functions

        A const-modified class member function is called a const member function. The const-modified class member function actually modifies the this pointer implicit in the class member function , indicating that the object pointed to by this pointer cannot be modified in the member function.

For example, for the Print function of the date class, we need to overload a const-modified version to print const-modified objects.

// 非const修饰
void Date::Print()
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
// const修饰版本
void Date::Print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

Date d1;
const Date d2(2023,9,10);
d1.Print(); // 调用Print()
d2.Print(); // 调用Print() const

In fact, const formal parameters can receive non-const actual parameters, so the Print function of the date class only needs to retain the const modified version.

For sequence tables, the return value of const-modified operator[] also needs to be modified with const to achieve read-only members of const objects.

// 只读
const int& SeqList::operator[](size_t i) const
{
	assert(i < _size);

	return _a[i];
}

// 读/写
int& SeqList::operator[](size_t i)
{
	assert(i < _size);

	return _a[i];
}

cosnt modification mainly involves the understanding of permissions. Think about the following questions:

1. Can a const object call non-const member functions? No
2. Can non-const objects call const member functions? Can
3. Can other non-const member functions be called within a const member function? No
4. Can other cosnt member functions be called within a non-cosnt member function? Can

explain:

1,3 A const object/member function calls a non-const member function, that is, passing the const-modified actual parameter (read-only) to a non-const formal parameter (read-write), which is an amplification of permissions and cannot be called.

2,4 A const object/member function calls a const member function, that is, passing non-const modified actual parameters (read and write) to a const formal parameter (read-only), which is a reduction of permissions and can be called.

3 Implement date class

        In the previous explanation, most of the functions of the date class have been implemented. Next, a more complete code of the date class is given, which mainly adds operator-function (date-date) and modifies the required member functions with const. You can refer to the code exercises.

Use separated compilation, declare the date class in the Date.h header file, and define the member functions of the class in the Date.cpp file.

Date.h file

#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 = 1970, int month = 1, int day = 1);

	// 拷贝构造函数
	Date(const Date& d);

	// 获取某年某月的天数
	int GetMonthDay(int year, int month);

	// 赋值运算符重载
	Date& operator=(const Date& d);

	// 日期+=天数
	Date& operator+=(int day);

	// 日期+天数
	Date operator+(int day) const;

	// 日期-=天数
	Date& operator-=(int day);

	// 日期-天数
	Date operator-(int day) const;


	// 前置++
	// d++ -> d.operator()
	Date& operator++();

	// 后置++
	// d++ -> d.operator(0)
	Date operator++(int);

	// 前置--
	Date& operator--();

	// 后置--
	Date operator--(int);


	// >运算符重载
	bool operator > (const Date& d) const;

	// ==运算符重载
	bool operator == (const Date& d) const;

	// >=运算符重载
	bool operator >= (const Date& d) const;

	// <运算符重载
	bool operator < (const Date& d) const;

	// <=运算符重载
	bool operator <= (const Date& d) const;

	// !=运算符重载
	bool operator != (const Date& d) const;

	// 日期-日期 返回天数
	int operator-(const Date& d) const;

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

Date.cpp file

#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 Date::GetMonthDay(int year, int month)
{
	static int MonthDay[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

	if (month == 2 && ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)))
		return 29;

	return MonthDay[month];
}

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;

	// 检查日期是否合法
	if (month < 1 || month > 12
		|| day < 1 || day > GetMonthDay(year, month))
	{
		cout << "非法日期" << endl;
	}
}

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

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

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 == 13)
		{
			_year++;
			_month = 1;
		}
	}

	return *this;
}

Date Date::operator+(int day) const
{
	Date tmp(*this);
	tmp += day;

	return tmp;
}

Date& Date::operator-=(int day)
{
	if (day < 0)
		return *this += (-day);

	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}

		_day += GetMonthDay(_year, _month);
	}

	return *this;
}

Date Date::operator-(int day) const
{
	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;
}

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

Date Date::operator--(int)
{
	Date tmp(*this);

	*this -= 1;

	return tmp;
}

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

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

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

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

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

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

int Date::operator-(const Date& d) const
{
	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;
}

If the content of this article is helpful to you, you can like it and save it. Thank you for your support and look forward to your attention.

Preview of the next article: C++ classes and objects (4) initialization list, static members, friends, inner classes

Guess you like

Origin blog.csdn.net/2301_79391308/article/details/132579955