C++: Classes and Objects (Part 2)

1. Six default member functions of the class:

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


Default member function: If the user does not implement it explicitly, the member function generated by the compiler is called the default member function
class Date {};

2. Constructor:

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 this method is called every time the object is created to set the
information, This is a bit troublesome. Can the information be set when the object is created?

        The constructor is a special member function with the same name as the class. It is automatically called by the compiler when creating a class type object to ensure that each data member has a suitable The initial value, and is only called 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 them. object.

The characteristics are as follows:1. The function name is the same as the class name.  2. No return value.  3. When the object is instantiated, the compiler automatically calls the corresponding constructor.   4. The constructor can be overloaded.
        
       
       
      

Note: If an object is created through a parameterless constructor, there is no need to follow the object with parentheses, otherwise it becomes a function declaration.

class Date
{
public:
    //Date()
    //{

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

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

    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    //Date d1();注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
    Date d1;
    d1.Print();

    Date d2(2023,10,31);
    d2.Print();

    return 0;
}

        5. If there is no explicit constructor defined in the class, the C++ compiler will automatically generate onewithout parameters< The default constructor of a i=3> will no longer be generated by the compiler once explicitly defined by the user.

Notice:

        If the user explicitly defines the constructor, the compiler will no longer generate it; (i.e. Write a constructor yourself) a>

        After shielding the constructor in the Date class, the code can be compiled because the compiler generates a parameterless default constructor;

        Let go of the function in the DATE class, and the code compile fails, because once it is explicitly defined any constructor, the compiler will not be reappeared into a non -ginseng constructor. There is no suitable default constructor available (because here needs a parameterless constructor after releasing it. After uncommenting, a default parameterless constructor will no longer be constructed, results in the inability to match the constructor)

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

        6. Regarding the default member functions generated by the compiler, many children will have doubts: 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 _year/_month/_day is still a random value. In other words, the default constructor generated by the compiler is of no use here? ?
        Answer: C++ divides types into built-in types (basic types) and custom types. Built-in types are data types provided by the language, such as: int/char..., custom types are types we define ourselves using class/struct/union, etc. If you look at the following program, you will find that the compiler generates a default structure The function will call its default member function on the custom type member _t. Built-in types are not processed, and the custom type compiler will call the default constructor.

Built-in types: int, char, double, ... and pointers

Custom types: For example, Date and Time above are our own defined types.

Note:C++11 has patched the defect of non-initialization of built-in type members, namely:Built-in type member variables Default value can be given when declared in
class.

That is, you can:

private:

        int _year = 10;

        int _month = 10;

        int _day = 10;

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

        7. The parameterless constructor and the fully default constructor are both called default constructors, and there can only be one default constructor.
Note: 1. No-argument constructor, 2. Full default constructor, 3. We did not write the constructor generated by the compiler by default, are all acceptable Considered a default constructor. You can call the constructor without passing parameters, which can be called the default constructor. These three functions cannot exist at the same time, only one can exist.

Summarize: 

1. Under normal circumstances, we have to write the constructor ourselves;

2. If the members are all custom types, or default values ​​are given when declaring, you can consider letting the compiler generate the constructor itself.

3. Destructor:

concept:

        Destructor: Contrary to the function of the constructor, the destructor does not complete the destruction of the object itself, Local object destruction is completed by the compiler. When the object is destroyed, the destructor will be automatically called to complete the cleanup of resources in the object.

characteristic:

The destructor is a special member function with the following characteristics:
        1. The name of the destructor is inAdd the characters ~ before the class name.
        2. isno parameter and no return value type.   3. A class can only have one destructor. If not explicitly defined, the system will automatically generate a default destructor.
      

Note: The destructor cannot be overloaded
        4. ObjectAt the end of its life cycle, 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);
}//结束自动调用析构函数

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

        5. Regarding the destructor automatically generated by the compiler, will it accomplish something? In the following program, we will see that the default destructor generated by the compiler calls its destructor for a 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;
}

Output after the program runs: ~Time(),
There is no object of Time class directly created in the main method. Why is the destructor of Time class called in the end?
        Because: the Date object d is created in the main method, and d contains 4 member variables, of which _year, _month,
_day are built-in types members, no resource cleanup is required when destroyed. In the end, the system can directly recycle its memory;And _t is a Time class object, so when d is destroyed, the Time class contained in it must be The _t object is destroyed, so the destructor of the Time class must be called.

However: the destructor of the Time class cannot be directly called in the main function. What is actually to be released is the Date class object, so the compiler will call the destructor of the Date class. If Date is not provided explicitly, the compiler will generate a default destructor for the Date class.The purpose is to call the destructor of the Time class internally, that is, when the Date object is destroyed , to ensure that each custom object inside it can be destroyed correctly. The main function does not directly call the Time class destructor, but explicitly calls the default destructor generated by the compiler for the Date class
Note: The destructor of that class is called when an object of that class is created, and the destructor of that class is called when an object of that class is destroyed

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

4.Copy constructor:

concept:

        Copy constructor copy constructor: There is only a single formal parameter, which is a reference to an object of this class type (generally commonly used with const modification). It 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 isAn overloaded form of the constructor.

        2. The parameter of the copy constructor has only one and must be a reference to the type object of the class, use The compiler directly reports an error when passing by value, because it willtrigger infinite recursive calls.

        3. If not explicitly defined, the compiler will generate a default copy constructor. The default copy constructor object completes the copy in byte order according to memory storage. This type of copy is called shallow copy. Or value copy.

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

        4. The default copy constructor generated by the compiler can already complete the byte order value copy. Do I still need to implement it explicitly by myself?
Of course, classes like date classes are not necessary. What about the following classes? Try verifying 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);

	Stack s2(s1);
	return 0;
}

Why do we crash in this code?

At this time,s1 and s2 both point to the same space opened by malloc. When the program ends, the destructor is called. First S2 is released, but s1 also needs to be released, so the same space is released twice, causing the program to crash! ! !

Note:In the classIf there is no resource application involved, the copy constructor can be written or not; once it involves resource application When a>
, the copy constructor must be written, otherwise it will be a shallow copy/value copy.

 The correct writing of the above code is:

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(const Stack& stt)
	{
		_array = (DataType*)malloc(sizeof(DataType) * stt._capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_array, stt._array, sizeof(DataType) * stt._size);
		_capacity = stt._capacity;
		_size = stt._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);

	Stack s2(s1);
	return 0;
}

5. Typical calling scenarios of copy constructor:
        1. Create a new object using an existing object
        2. The function parameter type is a class type object
        3. Function return value Type is 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;
}

        In order to improve program efficiency, generally try to use reference types when passing object parameters. When returning, use references as much as possible according to the actual scenario.

Summarize:

        1. Shallow copy/value copy: copy completed by built-in type members;

        2. Deep copy: The essence is the resource pointed to by Aoba, so that the new object has the same space and the same value as the copied object; the custom type member calls the copy constructor of this member;

        For example: Stack needs to write its own copy structure to complete deep copying. Sequence lists, linked lists, binary trees, etc. all need deep copying (need to open up resources!!!).

5. Assignment operator overloading:

Operator overloading:

C++ introduces operator overloading in order to enhance the readability of the code. Operator overloading is a function with a special function name. It also has its return value type, function name and parameter list. Its return value type and parameter list are the same as ordinary ones. The functions are similar.
The function name is: the keyword operator is followed by the operator symbol that needs to be overloaded.
Function prototype: return value type operator (parameter list)
Note:
        1. Cannot be connected by connecting other symbols Create a new operator: such as operator[operator] 
        2.The overloaded operator must have a class type parameter, For example: cannot use int operator+(int x, int y). This will not call the built-in +, resulting in changing the built-in operation rules
        3. Operators used for built-in types cannot change their meaning. For example: the built-in integer type "+" cannot change its meaning
        4. When overloaded as a class member function, its shape The parameters appear to be 1 less than the number of operands, because the first parameter of the member function is the hidden this
        5.[ .* ] [ :: ] [ sizeof ] [ ?: ] (三目Operator) [ . ] Note that the above five operators cannot be overloaded. This often appears in multiple-choice questions in written examinations
.

// 全局的operator==
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;
};
// 这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
// 这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。
bool operator==(const Date& d1, const Date& d2)
{
    //这个是类外定义!!!
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}

void Test()
{
	Date d1(2018, 9, 26);
	Date d2(2018, 9, 27);
	cout << (d1 == d2) << endl;
}

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

Here you will find that operator is overloaded into global Member variables need to be public, Then the question is, how to ensure encapsulation (not allowing others to use your own)?

Assignment operator overloading:

1. Assignment operator overloading format
        1.Parameter type:const T (class name)&, passing references can improve the efficiency of parameter passing
        2.Return value type : T (class name)&, returning a reference can improve the efficiency of return. The purpose of returning a value is to support continuous assignment
        3. Check whether you assign a value to yourself (operator>(&d1, d2))
        4.Return *this: must comply with 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;
	}

bool operator>(const Date& y)//(const Date* this,const Date& y)
	{
		if (_year > y._year)
		{
			return true;
		}
		else if (_year == y._year && _month > y._month)
		{
			return true;
		}
		else if (_year == y._year && _month == y._month && _day > y._day)
		{
			return true;
		}

		return false;
	}

bool operator==(const Date& y)//(const Date* this,const Date& y)
	{
		return _year == y._year
			&& _month == y._month
			&& _day == y._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    Date d1(2023,11,2);
    Dare d2(2000,1,1);
	bool ret1 = d1 > d2;  // operator>(d1, d2)-> d1.operator>(&d1, d2)
	bool ret2 = d1 == d2; // operator==(d1, d2)-> d1.operator==(&d1, d2)

}

 

2. The assignment operator can only be overloaded into a classmember function cannot be overloaded into Global function

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 =”必须是非静态成员

int main()
{
	Date d1(2000, 1, 1);
	Date d2(2000, 1, 2);

	(d1 = d2);

	return 0;
}

Cause:Assignment operatorIf not implemented explicitly, the compiler will generate a default one. At this time, the user implements a global assignment operator overload outside the class, which conflicts with the default assignment operator overload generated by the compiler in the class< /span>

operator overloading can only be a member function of the class. , so assignment,

Operator overloading and function overloading are not related!

        Operator overloading: Custom types can use operators directly

        Function overloading: Functions with the same name with different parameters can be allowed.

 operator= If we don’t write it, the compiler will generate the default operator =
 which is similar to the behavior of copy construction. The built-in type value is copied, and the custom type calls its assignment  Stack must implement operator= itself to achieve deep copy< /span>
 For example: Date MyQueue (using two stacks to implement a queue) does not need to be written, and operator = is generated by default and can be used

3. When the user does not explicitly implement it, the compiler will generate a default assignment operator overload and copy it byte by value in the form of value. (Shallow copy/value copy)

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 overloaded function generated by the compiler can complete byte order value copying, do you still need to implement it yourself
? Of course classes like date classes are not necessary. What about the following classes? Try verifying 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;
}

 

Note: If resource management is not involved in the class, the assignment operator can be implemented or not; once resource management is involved, it must
be implemented.

We know thatcout<<, cin>> are printing (stream insertion) and input (stream extraction), if we want Overloading of " << " and " >> "

At this time we see that " cout << d16 ' cannot pass, and d16 needs to be in front to pass, but this does not look smooth, so we needWritten as a global function, but cannot guarantee the encapsulation of member variables, so it is introduced at this time< a i=5>Friend function can ensure encapsulation. 

 

//cout <<
ostream& operator<<(ostream& out,Date& d)
{
	out << d._year << "-" << d._month << "-" << d._day << endl;

	return out;
}

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

	return in;
}

Summarize:

        Other operations are generally implemented as member functions, "<<" ">>" Stream operators must be implemented globally< a i=2>, so that can put the stream object in the first parameter position, which is readable.

analyze:

        The essence of stream is to solve the input and output problems of custom types. Printf and scanf cannot solve the input problems of custom types, so they are solved by: object-oriented + operator overloading.

Also note:

        Some people think that this will not conflict with left shift>> and right shift<<? This symbol here is double defined (two meanings), but note that the operator overloading condition is a custom type, and left shift and right shift are built-in types! ! !

Pre++ and post++ overloads:

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 前置++:返回+1之后的结果
	// 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
	Date& operator++()
	{
		_day += 1;
		return *this;
	}
	// 后置++:
	// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
	// C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
	//自动传递
	// 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存
	//一份,然后给this + 1
	// 而temp是临时对象,因此只能以值的方式返回,不能返回引用
	Date operator++(int)
	{
		Date temp(*this);
		_day += 1;
		return temp;
	}

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

int main()
{
	Date d;
	Date d1(2022, 1, 13);
	d = d1++; // d: 2022,1,13 d1:2022,1,14
	d = ++d1; // d: 2022,1,15 d1:2022,1,15
	return 0;
}

Prepend++: Return the result after +1
Note:The object pointed to by this will not be destroyed after the function ends, so it is referenced Method return improves efficiency
post++:

Prefix++ and postfix++ are both unary operators. In order to allow correct overloading of prefix++ and postfix++, C++ stipulates: When setting ++ overloadingAdd one more int type parameter, but this parameter does not need to be passed when calling the function, the compiler will automatically pass it< a i=4>Note: Postfix ++ is used first and then +1, so the old value before +1 needs to be returned, so this needs to be saved first during implementation , then give this+1 and temp is a temporary object, so it can only be returned as a value, not a reference.

6. Implementation of date class:

// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{
	assert(_year >= 1 && _month >= 1);
	int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

	if (_month == 2 && ((year % 4 == 0 && year != 100) || (year % 400 == 0)))
	{
		return 29;
	}
	return arr[month];
}

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

// 全缺省的构造函数
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;

	if (_year < 1 ||
		_month < 1 || _month > 12 ||
		_day < 1 || _day > GetMonthDay(_year, _month))
	{
		//assert(false);
		Print();
		cout << "日期非法" << endl;
	}
}

// 拷贝构造函数
// d2(d1)
Date::Date(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;

	if (_year < 1 || _month < 1 || _month > 12 || _day < 1 || _day > GetMonthDay(_year, _month))
	{
		方法一
		//exit(-1);
		方法二
		//Print();
		//cout << "输入格式错误" << endl;
		//方法三
		assert(false);
	}
}

// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& Date::operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;
}

// 析构函数
Date::~Date()
{
	_year = 0;
	_month = 0;
	_day = 0;
}

// 日期+=天数
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)
{
	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++()
{
	*this += 1;

	return *this;
}

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

	return tmp;
}

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

	return tmp;
}

// 前置--
Date& Date::operator--()
{
	*this -= 1;

	return *this;
}

// >运算符重载
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;
	}

	return false;
}

// ==运算符重载
bool Date::operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}

// >=运算符重载
bool Date::operator>= (const Date& d)
{
	if (*this > d || *this == d)
	{
		return true;
	}

	return false;

	return *this > d || *this == d;
}

// <运算符重载
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;
	//}

	//return false;
    
    //可以复用尽量复用
	return !(*this >= d);
}

// <=运算符重载
bool Date::operator<= (const Date& d)
{
	//if (*this < d || *this == d)
	//{
	//	return true;
	//}

	//return false;

	return !(*this > d);
}

// !=运算符重载
bool Date::operator!= (const Date& d)
{
	//if (*this == d)
	//{
	//	return false;
	//}

	//return true;

	return !(*this == d);
}

// 日期-日期 返回天数
int Date::operator-(const Date& d)
{
	Date max = d;
	Date min = *this;
	int flag = 1;

	if (*this > d)
	{
		max = *this;
		min = d;
		flag = -1;
	}
	int n = 0;
	while (min != max)
	{
		++min;
		++n;
	}

	return n * flag;
}

Here we need to understand that our programmerscan write more efficient code at work, so we can write more efficient code, as follows: < /span> 

7.const members:

        The "member function" modified by const is called a const member function, and const modified class The member function actually modifies the this pointer implicit in the member function, indicating that no member of the class can be modified in the member function.

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 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();//&d1:const Datr* d1
	const Date d2(2022, 1, 13);
	d2.Print();//&d2:const Datr* d2
}

int main()
{
	Test();

	return 0;
}

Add ' const ' after the member function, that is, add const before the parameters passed by , Generally used when the passed parameters are not changed: +, -, judgment...

Member function definition principles:

1. Member functions that can be defined as const should be defined as const, so that both const objects and non-objects can be called (ie: permission translation or permission reduction)

2. To modify the member function of a member variable, it cannot be defined as const.

8. Overloading of address-taking and const address-taking operators:

The following two default member functions generally do not need to be redefined, and the compiler will generate them by default.
Note: Only in special circumstances, overloading is required , such as if you want others to get the specified content!

	Date* operator&()
	{
		return this;
	}

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

Special case:

We will create it ourselves only when we don’t want others to know this address. At this time:

        1. Return a fake address;

        ​ ​ 2. Return a null pointer

	Date* operator&()
	{
		//return this;

		//给一个假地址
		return (Date*)0x1111111;
	}

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

		//不给看地址
		return nullptr;
	}

 The above is my personal learning insights and analysis. You are welcome to discuss it in the comment area!

Thank you guys for the one-click triple connection! Thank you guys for the one-click three-way connection! Thank you guys for the one-click three-way connection!

                                              

Guess you like

Origin blog.csdn.net/weixin_71964780/article/details/134139907