Chapter 2: Classes and Objects (2)

Series Article Directory



foreword

Classes and objects are very important concepts in C++.


The 6 default member functions of the class

If there are no members in a class, it is simply called an empty class.
Is there really nothing in the empty class? No, when any class does not write anything, the compiler will automatically generate the following 6 default member functions.

Default member function : The member function generated by the compiler without explicit implementation by the user is called the default member function.

class Date {
    
    };

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-rzUlDI7y-1689232150450)(https://flowus.cn/preview/e12a5abb-e485-4430-b678-df585a1dde04)]

Constructor

concept

class Date
{
    
    
public:
	void Init(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1;
	d1.Init(2022, 7, 5);
	d1.Print();
	Date d2;
	d2.Init(2022, 7, 6);
	d2.Print();
	return 0;
}

For the Date class, you can set the date for the object through the Init public method, but if you call this method to set the information every time the object is created, it is a bit troublesome. Can you set the information when the object is created?

The constructor is a special member function with the same name as the class name. It is automatically called by the compiler when creating a class type object to ensure that each data member has an appropriate initial value, and it is called only once during the entire life cycle of the object .

characteristic

The constructor is a special member function. It should be noted that although the name of the constructor is called construction, the main task of the constructor is not to open space to create objects, but to initialize objects.

Its characteristics are as follows:

  1. The function name is the same as the class name

  2. No return value, not void, but no need to write the return value type

  3. When the object is instantiated, the compiler automatically calls the corresponding constructor, which cannot be called by the object

  4. Constructors can be overloaded, a class can have multiple constructors

class Date
{
    
    
public:

    //两种显示构造函数
	// 1.无参构造函数
	Date()
	{
    
    }
	// 2.带参构造函数
	Date(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestDate()
{
    
    
    //定义了对象,并初始化了对象
	Date d1; // 调用无参构造函数,不需要(),如果带上就会让编译器分不清是不是函数声明
	Date d2(2015, 1, 1); // 调用带参的构造函数
	// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
	// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
	// warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
	Date d3();
}

  1. If no constructor is explicitly defined in the class, the C++ compiler will automatically generate a parameterless default constructor. Once the user explicitly defines the compiler will no longer generate it. The default constructor given by the compiler does not handle built-in type members, and calls its default constructor for custom type members.
class Date
{
    
    
public:
	/*
	// 如果用户显式定义了构造函数,编译器将不再生成
	Date(int year, int month, int day)
	{
	_year = year;
	_month = month;
	_day = day;
	}
	*/
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	// 将Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
	// 将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成
	// 无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用
		Date d1;
	    return 0;
}
  1. Many children's shoes have doubts about the default member functions generated by the compiler: if the constructor is not implemented, the compiler will generate a default constructor. But it seems that the default constructor is useless?

The d object calls the default constructor generated by the compiler,
but the d object is still a random value like _year/_month/_day.
In other words, the default constructor generated by the compiler is not useful here

Answer:
C++ divides types into built-in types (basic types) and custom types. The built-in type is the data type provided by the language, such as: int/char..., and the custom type is the type defined by ourselves such as class/struct/union we use.

Looking at the following program, you will find that the default constructor generated by the compiler calls its default member function for the custom type member _t.

class Time
{
    
    
public:
	Time()
	{
    
    
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
    
    
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};
int main()
{
    
    
	Date d;
	return 0;
}

Note: In C++11, a patch has been patched for the defect of non-initialization of built-in type members, that is, built-in type member variables can be given default values ​​when declared in a class.

class Time
{
    
    
public:
	Time()
	{
    
    
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
    
    
private:
	// 基本类型(内置类型),声明位置给的缺省值
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
    
    
	Date d;
	return 0;
}
  1. Both the parameterless constructor and the default constructor are called default constructors, and there can only be one default constructor.

Note: No-argument constructors , full default constructors , and constructors that we did not write by default generated by the compiler can all be considered as default constructors, constructors that can be called without passing parameters .

class Date
{
    
    
public:
	Date()
	{
    
    
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
// 以下测试函数能通过编译吗?
void Test()
{
    
    
	Date d1;
}

As a general recommendation, every class provides a default constructor.

destructor

concept

Through the study of the previous constructor, we know how an object came from, and how did that object disappear?

Destructor: Contrary to the function of the constructor, the destructor does not complete the destruction of the object itself, and the local object destruction is done by the compiler .
When the object is destroyed, it will automatically call the destructor to complete the cleanup of resources in the object.

characteristic

A destructor is a special member function whose characteristics are as follows:

  1. The destructor name is prefixed with the character ~ in front of the class name

  2. no parameters no return type

  3. A class can have only one destructor. If not explicitly defined, the system will automatically generate a default destructor.
    The default destructor does not handle built-in type members, and its destructor is called for custom type members.
    Note: Destructors cannot be overloaded

  4. When the object life cycle ends, the C++ compilation system automatically calls the destructor

typedef int DataType;
class Stack
{
    
    
public:
	Stack(size_t capacity = 3)
	{
    
    
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
    
    
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
    
    
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	// 其他方法...
	~Stack()
	{
    
    
		if (_array)
		{
    
    
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
void TestStack()
{
    
    
	Stack s;
	s.Push(1);
	s.Push(2);
}
  1. Does something get done with regards to the destructors automatically generated by the compiler? In the following program, we will see that the default destructor generated by the compiler calls its destructor for the custom type member.
class Time
{
public:
	~Time()
	{
		cout << "~Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}
 //程序运行结束后输出:~Time()
 //在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?
 //因为:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month,_day三个是
 //内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在
 //d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。但是:main函数
 //中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函
 //数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time
 //类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁
 //main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数
 //注意:创建哪个类的对象则调用该类的构造函数,销毁那个类的对象则调用该类的析构函数

Two stacks implement a queue

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-CP6Sif7C-1689232150451)(https://flowus.cn/preview/d02ad155-2100-4033-99c5-8cffd5f9aef0)]

  1. If there is no resource application in the class, the destructor can not be written, and the default destructor generated by the compiler is used directly, such as the Date class; when there is a resource application, it must be written, otherwise it will cause resource leakage, such as the Stack class.

copy constructor

concept

When creating an object, can you create a new object that is the same as an existing object?

Copy constructor: There is only a single formal parameter , which is a reference to the object of this class type (usually const decoration is commonly used) , and is automatically called by the compiler when creating a new object with an existing class type object .

feature

The copy constructor is also a special member function with the following characteristics:

  1. The copy constructor is an overloaded form of the constructor .

  2. The parameter of the copy constructor is only one and must be a reference to a class type object , and the compiler will directly report an error if the value-passing method is used , because it will cause infinite recursive calls.

For built-in types, the compiler can directly copy (pass parameters by value) for function passing parameters; for
custom types, function passing parameters need to call copy construction.

class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	// Date(const Date& d) // 正确写法
	Date(const Date d) // 错误写法:编译报错,会引发无穷递归
	{
    
    
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1;
	Date d2(d1);
    //Date d2 = d1;同上一句,也是调用拷贝构造
	return 0;
}

The transfer of the external link image failed. The source site may have an anti-theft link mechanism. It is recommended to save the image and upload it directly (img-oEu587eF-1689232150452) (https://flowus.cn/preview/eab2c3dd-e585-4cd9-ab84-a62238ed52fe)

  1. If not explicitly defined, the compiler will generate a default copy constructor.
    The default copy constructor object is copied in byte order according to memory storage. This kind of copy is called shallow copy, or value copy.
class Time
{
    
    
public:
	Time()
	{
    
    
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
	Time(const Time& t)
	{
    
    
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		cout << "Time::Time(const Time&)" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
    
    
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};
int main()
{
    
    
	Date d1;
	// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
	// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数
	Date d2(d1);
	return 0;
}

Note: In the default copy constructor generated by the compiler, the built-in type is directly copied in byte mode, while the custom type is copied by calling its copy constructor.

  1. The default copy constructor generated by the compiler can already copy byte-ordered values. Do I need to explicitly implement it myself?
    Of course classes like the Date class are unnecessary. What about the following classes? Try to verify it?
typedef int DataType;

class Stack
{
    
    
public:
	Stack(size_t capacity = 10)
	{
    
    
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
    
    
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
    
    
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
    
    
		if (_array)
		{
    
    
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};

int main()
{
    
    
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}

[External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-CzjMKmU2-1689232150452)(https://flowus.cn/preview/db2eded1-cfae-4231-a391-43a7b33ae850)]

Note: If there is no resource application involved in the class, the copy constructor can be written or not; once the resource application is involved, the copy constructor must be written, otherwise it is a shallow copy.

If you implement the destructor to free up space, you need to implement copy construction.

  1. Typical calling scenarios of copy constructor:
  • Create a new object using an existing object

  • The function parameter type is a class type object

  • The return value type of the function is a class type object

class Date
{
    
    
public:
	Date(int year, int minute, int day)
	{
    
    
		cout << "Date(int,int,int):" << this << endl;
	}
	Date(const Date& d)
	{
    
    
		cout << "Date(const Date& d):" << this << endl;
	}
	~Date()
	{
    
    
		cout << "~Date():" << this << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

Date Test(Date d)
{
    
    
	Date temp(d);
	return temp;
}

int main()
{
    
    
	Date d1(2022, 1, 13);
	Test(d1);
	return 0;
}

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-Qsbp1fbf-1689232150453)(https://flowus.cn/preview/8ce10ba6-2a0c-4c00-ae61-7e6b16036cd7)]

In order to improve the efficiency of the program, when passing parameters to general objects, try to use the reference type as much as possible , and use references as much as possible according to the actual scene when returning.

  1. Copy construction and assignment overloads are generated by default

The built-in type completes shallow copy/value copy – copying the custom type one by one by byte will call this member copy construction/assignment overload

typedef int DataType;

class Stack
{
public:
    //构造函数
	Stack(size_t capacity = 10)
	{
		cout << "Stack(size_t capacity = 10)" << endl;

		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			exit(-1);
		}

		_size = 0;
		_capacity = capacity;
	}
    
	void Push(const DataType& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
    
    //拷贝构造函数
	Stack(const Stack& st)
	{
		cout << "Stack(const Stack& st)" << endl;
		_array = (DataType*)malloc(sizeof(DataType)*st._capacity);
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			exit(-1);
		}

		memcpy(_array, st._array, sizeof(DataType)*st._size);
		_size = st._size;
		_capacity = st._capacity;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;

		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}

private:
	DataType *_array;
	size_t    _size;
	size_t    _capacity;
};

class MyQueue
{
public:
	// 默认生成构造
	// 默认生成析构
	// 默认生成拷贝构造

private:
	Stack _pushST;
	Stack _popST;
	int _size = 0;
};

int main()
{
	Stack st1;
	st1.Push(1);
	st1.Push(2);
	st1.Push(4);
	Stack st2(st1);
	cout << "=============================" << endl;

	MyQueue q1;//自定义类型调用构造函数
	MyQueue q2(q1);//自定义类型调用拷贝构造函数


	return 0;
}

assignment operator overloading

operator overloading

C++ introduces operator overloading to enhance the readability of the code. Operator overloading is a function with a special function name , and also has its return value type, function name, and parameter list. The return value type and parameter list are similar to ordinary functions.

The function name is: the keyword operator followed by the operator symbol that needs to be overloaded.

Function prototype: return value type operator operator (parameter list)

Notice:

  • Operator overloading: Objects of custom types can use operators

  • New operators cannot be created by concatenating other symbols: e.g. operator@

  • An overloaded operator must have a class type parameter

  • Operators used for built-in types, whose meaning cannot be changed, for example: built-in integer +, whose meaning cannot be changed

  • When overloaded as a class member function, its formal parameters appear to be 1 less than the number of operands, because the first parameter of the member function is the hidden this

  • .* :: sizeof ?: . Note that the above 5 operators cannot be overloaded. This often appears in written multiple choice questions


class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	//private:
	int _year;
	int _month;
	int _day;
};
// 全局的operator==
// 这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
// 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。
bool operator==(const Date& d1, const Date& d2)
{
    
    
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}
class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	// bool operator==(Date* this, const Date& d2)
	// 这里需要注意的是,左操作数是this,指向调用函数的对象
	bool operator==(const Date & d2)
	{
    
    
		return _year == d2._year
		&& _month == d2._month
			&& _day == d2._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    
    
	Date d1(2018, 9, 26);
	Date d2(2018, 9, 27);
	cout << (d1 == d2) << endl;
   //d1 == d2  =>  operator==(d1, d2)
   //d1 == d2  =>  d1.operator==(d2)
}

assignment operator overloading

assignment operator overload format

  • Parameter type : const T& , passing by reference can improve the efficiency of parameter passing

  • Return value type : T& , return reference can improve the efficiency of return, and the purpose of return value is to support continuous assignment

  • Check if you assign a value to yourself

  • Return *this : to compound the meaning of continuous assignment

class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d)
	{
    
    
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	Date& operator=(const Date& d)
	{
    
    
		if (this != &d)
		{
    
    
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

Assignment operators can only be overloaded as member functions of classes and cannot be overloaded as global functions

class Date
{
    
    
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	int _year;
	int _month;
	int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{
    
    
	if (&left != &right)
	{
    
    
		left._year = right._year;
		left._month = right._month;
		left._day = right._day;
	}
	return left;
}
// 编译失败:
// error C2801: “operator =”必须是非静态成员

Reason: If the assignment operator is not explicitly implemented, the compiler will generate a default assignment overload. At this time, if the user implements a global assignment operator overload outside the class, it will conflict with the default assignment operator overload generated by the compiler in the class, so the assignment operator overload can only be a member function of the class.

insert image description here

When the user does not explicitly implement, the compiler will generate a default assignment operator overload, which is copied byte-by-byte in the form of value

Note: Built-in type member variables are directly assigned, while custom type member variables need to call the assignment operator overload of the corresponding class to complete the assignment.

class Time
{
    
    
public:
	Time()
	{
    
    
		_hour = 1;
		_minute = 1;
		_second = 1;
	}
    //显示赋值运算符重载
	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 Date
{
    
    
private:
	// 基本类型(内置类型)
	int _year = 1970;
	int _month = 1;
	int _day = 1;
	// 自定义类型
	Time _t;
};

int main()
{
    
    
	Date d1;
	Date d2;
	d1 = d2;
	return 0;
}

Now that the default assignment operator overloading function generated by the compiler can already complete byte-ordered value copying, do I still need to implement it myself? Of course classes like the Date class are unnecessary. What about the following classes? Try to verify it?

typedef int DataType;
class Stack
{
    
    
public:
	Stack(size_t capacity = 10)
	{
    
    
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
    
    
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
    
    
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
    
    
		if (_array)
		{
    
    
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
    
    
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2;
	s2 = s1;
	return 0;
}

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-VzhiD5jj-1689232150454)(https://flowus.cn/preview/d657cbf2-a2c1-4b28-9d4c-635af0e31768)]

Note: If resource management is not involved in the class, it doesn't matter whether the assignment operator is implemented; once resource management is involved, the implementation must be displayed.

Difference between assignment overloading and copy construction

Date d1(2024, 2, 4);
Date d2(2023, 3, 4);
Date d3(2025, 3, 4);


d3 = d2 = d1; // 赋值重载

Date d5 = d1; // 拷贝构造
Date d6(d1); // 拷贝构造 

Copy construction is the initialization of an object that has not yet been instantiated, and assignment overloading is the assignment of an object that has already been instantiated.

Implementation of the date class

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

class Date
{
    
    
	//友元函数
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& cin, Date& d);

public:
	Date(int year = 1900, int month = 1, int day = 1);
	void Print() 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;
	bool operator>=(const Date& d) const;

	Date& operator=(const Date& d);

	Date& operator+=(int day);
	Date operator+(int day) const;
	Date& operator-=(int day);
	Date operator-(int day) const;

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

	Date& operator++();
	Date operator++(int);//int参数仅仅是为了占位,跟前置重载区分
	Date& operator--();
	Date operator--(int);

	int GetMonthDay(int year, int month);


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

inline ostream& operator<<(ostream& out, const Date& d)
{
    
    
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;

	return out;
}

inline istream& operator>>(istream& in, Date& d)
{
    
    
	in >> d._year >> d._month >> d._day;

	return in;
}




Date.cpp
#include "Date.h"

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

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

}

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) const
{
    
    
	Date ret = *this;
	ret += day;
	
	return ret;
}

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) const
{
    
    
	Date tmp = *this;
	tmp -= day;
	return tmp;
}

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

int operator-(const Date& d)
{
    
    
	return 1;
}

void Date::Print() const
{
    
    
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

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

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

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

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

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

	return *this;
}

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

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

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

In the current file, if there is only a statement without a definition, it needs to be linked; if there is a definition directly, no link is required.
The short functions in the class are suitable for inline functions, which are directly defined in the class

const member

The const-modified "member function" is called a const member function . The const-modified class member function actually modifies the implicit this pointer of the member function , indicating that any member of the class cannot be modified in the member function .

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-D1IW5Fhp-1689232150454)(https://flowus.cn/preview/ea012759-735e-497e-9f0b-d543051a6a5c)]

class Date
{
    
    
public:
	Date(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
    //const 修饰 *this
    //Date* this => const Date* this 权限缩小
	void Print() const
	{
    
    
		cout << "Print()const" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
void Test()
{
    
    
	Date d1(2022, 1, 13);
	d1.Print();
	const Date d2(2022, 1, 13);
	d2.Print();
}
  1. Can a const object call a non-const member function?
    cannot

  2. Can a non-const object call a const member function?
    Can

  3. Can const member functions call other non-const member functions?
    Can

  4. Can non-const member functions call other const member functions?
    cannot

It is best to add const to member functions that do not change member variables internally, so that both const objects and ordinary objects can be called.

Address and const address operator overloading

These two default member functions generally do not need to be redefined, and the compiler will generate them by default.

class Date
{
    
    
public:
	Date* operator&()
	{
    
    
		return this;
	}
	const Date* operator&() const
	{
    
    
		return this;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

These two operators generally do not need to be overloaded, just use the default address overload generated by the compiler. Only in special cases, overloading is required, such as wanting others to obtain the specified content!

Summarize

Classes and objects are one of the very important concepts of C++.
A galloping horse can't take ten steps in one leap; a rough horse can ride ten times, and the merits can't be given up. —— Xun Kuang

Guess you like

Origin blog.csdn.net/yanyongfu523/article/details/131703145