10000 word summary of C + + classes and objects [source code analysis of date class]

Classes and objects

Classes and objects < upper >

object-oriented

It has always been process oriented programming, such as C language. Until the 1970s, process oriented programming showed deficiencies in developing large programs. The computer community put forward object-oriented programming, in which the core concepts are class and object, and the three characteristics of object-oriented are encapsulation, inheritance and polymorphism.

Process oriented and object-oriented are only two ideas with different emphases in computer programming. Process oriented is one of the most practical ways of thinking, of which the most important is the modular idea. Process oriented pays more attention to the steps of process, action or event. Even object-oriented also contains the idea of process-oriented. Compared with process-oriented, object-oriented method mainly objectifies things, and believes that things can be transformed into a series of objects and the relationship between them, which is more in line with people's cognitive way of things.

Taking the takeout system as an example, the process oriented idea will modularize the steps of ordering, taking, delivering and receiving orders one by one, which is reflected in the program one by one. The object-oriented idea will boil down the whole process to the relationship between objects, that is, the relationship between merchants, riders and users, which is embodied in the program, that is, class design.

Object oriented is a broad and profound idea, which cannot be understood thoroughly in a moment and a half. It needs to be understood slowly in learning and work.

Unlike Java, C + + is a pure object-oriented language. C + + is object-oriented but compatible with C, so it can also be process oriented.

 

1. Definition of class

//C
struct Student {
	char name[20];
	int age;
	int id;
};
struct Student s;
strcpy(s.name, "yyo");
s.age = 18;
s.id = 11;
//C++
struct Student {
	//Member variable
	char _name[20];
	int _age;
	int _id;
	//Member method
	void Init(const char* name, int age, int id) {
		strcpy(_name, name);
		_age = age;
		_id = id;
	}
	void Print() {
		cout << _name << endl;
		cout << _age << endl;
		cout << _id << endl;
	}
};
s1.Init("yyo", 19, 1);
s1.Print();
s2.Init("yyx", 18, 2);
s2.Print();

As can be seen from the above code, in C language, only variables can be defined in the structure, which is equivalent to a collection of multiple variables, and the way to operate member variables is more cumbersome and prone to errors than C + +.

Since C + + is compatible with C, there are two keywords for defining a class in C + +, namely struct and class. The structure is also upgraded to a class in C + +, and the class name can be used directly as a type. The difference between a class and a struct is that a class can define not only variables, but also methods or functions.

In C + +, more classes are defined by class. Classes defined by class and classes defined by struct have slightly different access permissions.

class className {
    // ...
};

Class is the keyword of the class, className is the name of the class, and the content in {} is the class body. The elements in a class, namely variables and functions, are called class members. The member variables of a class are called class attributes or class data, and the functions of a class become class methods or member functions.

 

2. Encapsulation of class

Object oriented programming pays attention to the word "encapsulation". Encapsulation is reflected in two aspects: one is to encapsulate data and methods in classes, and the other is to add restrictions on access rights to members.

2.1 access qualifier

C + + has three access qualifiers: public, protect and private.

  • Members decorated with public can be accessed directly outside the class, while members decorated with private and protect cannot be accessed directly outside the class.
  • The default access permission of members in class is private, and the default access permission in struct class is public.
  • The scope of the access qualifier is from the location where the access qualifier appears to the location where the next access qualifier appears.

Compared with public, private and protect are similar here, and their specific differences will be discussed in subsequent inheritance. The significance of encapsulation is to standardize the access rights of members. The reason to release the permissions of struct class is to be compatible with C.

The significance of encapsulation is to standardize the access rights of members and better manage the members of the class. It is generally recommended to mark the access rights of members clearly instead of using the default rules of the class.

class Student {
private:
	//Member variable
	char _name[20];
	int _age;
	int _id;
public:
	//Member method
	void Init(const char* name, int age, int id) {
		strcpy(_name, name);
		_age = age;
		_id = id;
	}
	void Print() {
		cout << _name << endl;
		cout << _age << endl;
		cout << _id << endl;
	}
};

Note that the access qualification modifier works only at compile time and has no effect on variables and functions after that.

Class 2.2 packaging

The three characteristics of object-oriented are encapsulation, inheritance and polymorphism. The learning stage of classes and objects only emphasizes the encapsulation mechanism of classes and objects. The definition of encapsulation is to organically combine data and methods of operating data into classes, hide the attributes and implementation details of objects, and only expose the interactive interface.

The essence of encapsulation is a management mechanism. Compared with the data structure implementation of C language version, it is dangerous and error prone to not encapsulate and expose all the members of the structure, but it is not error prone to call the interface provided by the structure. Generally, it is not allowed to easily operate outside the function and change the structure, which is the advantage of encapsulation. Process oriented programming only aims at function encapsulation, while object-oriented programming puts forward a more comprehensive encapsulation mechanism, which makes the code safer and easier to operate.

class Stack {
public:
	void Init();
	void Push(STDataType x);
	void Pop();
	STDataType Top();
	int Size();
	bool Empty();
	void Destroy();
private:
	STDataType* _a;
	int _top;
	int _capacity;
};

 

3. Use of class

3.1 scope of class

Class defines a new scope, and all members in the class are in the scope of the class.

  1. If the function body is defined directly in the class, the compiler will treat the functions defined in the class as inline functions by default, provided that the requirements of inline functions are met.
  2. When defining a member function outside a class, you need to use the scope qualifier:: to indicate the class domain to which the member belongs. As shown in the figure:

In general, the separation of declaration and definition is adopted as in the period of data structure.

3.2 class instantiation

The process of creating objects with classes is called class instantiation.

  1. Class is just a "model", which defines the nature of the class, but does not allocate space for it.
  2. Multiple objects can be instantiated by the class. The object occupies the actual space in memory and is used to store class member variables.

The relationship between classes and objects, just like the relationship between types and variables, can be understood as the relationship between drawings and houses.

 

4. Storage of class objects

Since there are both member variables and member functions in a class, what does a class object contain? How are class objects stored?

class Stack {
public:
	void Init();
	void Push(int x);
	// ...
private:
	int* _a;
	int _top;
	int _capacity;
};
Stack st;
cout << sizeof(Stack) << endl;
cout << sizeof(st) << endl;

If the class member function is also stored in the object, when multiple objects are instantiated, the member variables of each object are independent of each other, but the member function is the same, and multiple copies of the same code are stored, which wastes space. Therefore, only class variables are stored in C + + objects, and member functions are stored in public code segments.

The size of a class is the sum of the member variables in the class. Memory alignment is required, which is the same as the structure. Note that the size of the empty class is 1 byte, which is used to identify the existence of this object.

If the size of an empty class is 0, it means that there is no space allocated for the object created by this class in memory, which is equivalent to that the object does not exist, so it is impossible.

 

Next, we use stack and date classes to understand the knowledge in classes and objects.

5. this pointer

class Date {
public:
	void Init(int year, int month, int day) {
		//year = year;//Err
		//1.
        _year = year;
        //2.
        Date::month = month;
        //3.
		this->day = day;
	}
private:
	int _year;
	int month;
	int day;
};

If the member variable and the formal parameter have the same name, the assignment in Init function will give priority to the formal parameter, resulting in the member variable not being initialized. There are three solutions to this problem:

  1. Add _ before the member variable name, To distinguish between members and formal parameters.
  2. Use the domain access modifier::, to specify that the preceding variable is a member variable.
  3. Use the this pointer.

5.1 definition of this pointer

d1._year; The meaning of is to tell the compiler to find variables in D1 this object_ Year's address. But the function is not stored in the class object, which is D1 Print(); What is the meaning of?

As shown in the figure, D1 and D2 call the Print function stored in the common code area. Different objects are not distinguished in the function body. How to distinguish the calls of different objects?

This problem is solved by introducing this pointer in C + +. The C + + compiler adds a hidden parameter called this pointer to each non static member function. The this pointer points to the current calling object. All the operations on the member variables in the function body are accessed through the pointer, but these operations are automatically completed by the compiler and need not be actively transmitted.

As shown in the figure, when passing parameters, the pointer of the object is implicitly passed in, the object pointer is correspondingly hidden and added in the formal parameter list, and the this pointer is also hidden in front of the member variable in the function body.

5.2 characteristics of this pointer

this is a keyword in C + +, which represents the pointer of the current object. this pointer is the first implicit pointer parameter of the member function. It is generally passed by the register without active parameter transfer.

  1. When calling a member function, the this pointer cannot be explicitly passed in, and the declared this pointer cannot be displayed in the parameter list of the member function.

  2. However, this pointer can be used explicitly in member functions.

  3. The type of this is classType* const. Const is added to prevent this pointer from being changed.

  4. This pointer is essentially a formal parameter of a member function. When the function is called, the object address is passed into the pointer, so this pointer is a formal parameter stored in the function stack frame, and this pointer is not stored in the object.

Example 1 and 2, which will go wrong and what will go wrong?

class A {
public:
	void Printa() {
		cout <<  _a << endl;
	}
	void Show() {
		cout << "Show()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* a = nullptr;
	//1.
	a->Show();
	//2.
	a->Printa();
	return 0;
}
  • The function is not stored in the object, so the calling function will not access the null pointer a, but the null pointer is passed into the member function as a parameter. There are no program syntax errors, so the compilation must pass.
  • Calling the Show() function does not access the content in the object, and there is no problem of accessing null pointers. To call the Print() function, you need to access the member in the object indicated by the a pointer_ a. So the program accessing null pointers crashed.

Classes and objects < medium >

Default member function

An object must be initialized, free up space, copy and copy, etc. for example, if the stack structure is not initialized, an error will be reported if the stack is pressed directly. Because these operations are often used or essential, they are put into the class as the default generated member function at the beginning of design, which solves some shortcomings of C language.

The mechanism of C + + in designing the default member function of a class is complex. A class has six default member functions, namely constructor, destructor, copy constructor, assignment operator overload, t * operator & () and const t * operator & () const. They are special member functions that cannot be called as regular functions.

The default meaning is that if we don't write the compiler, it will automatically generate a copy in the class. If we write the compiler, it won't be generated. Automatically generate the default function. Sometimes the function is not comprehensive enough, you still have to write it yourself.

1. Constructor

1.2 definition of constructor

Constructor and destructor are used to initialize and clean up resources respectively. The constructor is equivalent to the initialization Init function we wrote during the data structure period.

Constructor is a special function with the same name as the class name. When creating a class object, it is automatically called by the compiler to initialize each member variable, and it is called only once in the life cycle of the object.

2.2 characteristics of constructors

Although constructors are called constructors, their job is not to open up space to create objects, but to initialize member variables in objects.

  • The function name is the same as the class name, and there is no return type.
  • When an object is instantiated, the compiler automatically calls its corresponding constructor.
  • Constructor supports function overloading.
//Call a parameterless constructor
Date d1;
Date d2(); //Err - function declaration
//Call the constructor with parameters
Date d2(2020,1,18);

Note that the constructor can only be called when the object is instantiated, and the parameterless constructor cannot be called with parentheses, otherwise it will be declared as a function.

  • If there is no explicit constructor defined in the class, the constructor created by the program by default is parameterless and has no return type. Once the compiler is explicitly defined, it is not generated.

  • Parameterless constructors, fully default constructors and default generated constructors can all be default constructors (constructors that can be called without passing parameters), and there can only be one default constructor to prevent conflicts.

Default constructor initialization rules

As can be seen from the above figure, the default generated constructor does not effectively initialize the member variables of built-in types. In fact, the compiler's default constructor only initializes the custom type, initializing it by calling its constructor after creating the member variable of the custom type. If the class of the custom type is also the default generated constructor, the result is naturally not effectively initialized.

  • The default generated constructor does not handle the member variables of built-in types. For custom type members, their constructors will be called to initialize custom type member variables.

It is better to have a default constructor in a class, because when the class object is treated as a member of other classes, the system will only call the default constructor.

At present, we only understand and master the basic usage, and we will talk about the constructor later.

 

2. Destructor

Destructor is also a special function, which is responsible for cleaning up and destroying resources in some classes.

2.1 definition of destructor

Contrary to the function of the constructor, the destructor is responsible for destroying and cleaning up resources. However, the destructor does not destroy the object. The object is a local variable in the main function stack frame, so it is created and destroyed with the main function stack frame. The destructor will be called automatically when the object is destroyed. It mainly cleans up some member variables created in the object, such as dynamically opened space, etc.

2.2 characteristics of destructors

  • The name of the destructor is ~ plus the class name. It also has no parameters and no return type, so overloading is not supported.
  • There is only one destructor in a class. Similarly, if it is not explicitly defined, the compiler automatically generates the default destructor.
  • At the end of the object life cycle, the system automatically calls the destructor to complete the cleanup.
  • The order in which multiple objects call destructors is opposite to the order in which objects are created, because which object is stacked first and which object is destroyed later.

The destructor is automatically called after the object is called. This mechanism can avoid forgetting to free space to avoid memory leakage. Not all classes need destructors, but it is convenient for some classes such as stack.

Default destructor cleanup rule

Similar to the default generated constructor, the default generated destructor does not process the member variables of the built-in type. Only when the object is destroyed, its destructor will be called on the members of the custom type to clean up the member variables of the custom type.

If the user-defined type member also has only the destructor generated by the system by default, the result is that the user-defined type member has not been destroyed.

It also makes sense not to release members of built-in types to prevent the release of some file pointers and so on from causing the program to crash.

 

3. Copy constructor

In addition to initialization and destruction, the most common is to copy an object when assigning a value and passing parameters to an object. The direct assignment of complex types such as class does not work. The operation of copying an object should be implemented by the copy constructor. Each time the object is copied, the copy constructor should be called.

3.1 definition of copy constructor

According to the requirements, we can also guess the design of copy constructor in C + +.

The copy constructor is also a special member function, which is responsible for the copy assignment of objects. This operation can only occur when objects are instantiated. The essence of copy construction is to initialize new objects with objects of the same type. Therefore, it is a different form of constructor to meet the requirements of overloading, which can also be called copy constructor.

The copy constructor has only one parameter, that is, the reference of an object of the same type, which is automatically called by the compiler when initializing a new object with an object of the same type. The copy constructor is also a constructor, so copy is also an overload of the construct.

3.2 characteristics of copy constructor

  • The copy constructor is an overloaded form of the constructor.
  • The copy constructor has only one parameter and must be a reference to an object of the same type, otherwise infinite recursion will be caused.

Because the value passing call needs to copy a temporary copy of the object, and to copy the object, you must call the copy constructor, and the copy constructor needs to pass the value call, so the "logical loop" will not come out in the call parameter list.

The default copy constructor generated by the system has been modified when designing the copy constructor, so no copy operation can occur in this process. The transfer of references does not involve copy operations, so there is no problem.

In addition, interestingly, the designer stipulates that the parameters of the copy constructor must be references of the same type. If it is designed as a pointer, the system will be deemed that there is no explicit definition of the copy constructor.

  • Generally, when copying and constructing another object, you don't want the original object to change, so the formal parameter reference is decorated with const.
  • Only the copy constructor is explicitly defined, and the system will not generate the default constructor. Only the constructor is defined, and the system will generate the copy constructor by default.
Default copy construction copy rules
  • If the copy constructor is not explicitly defined, similar to the constructor, the copy constructor generated by default can copy members in two ways:
  1. For member variables of built-in type, the copy structure generated by default copies the stored contents of the member to the new object byte by byte in byte order. Such a copy is called a shallow copy or a value copy. Similar to the memcopy function.
  2. For a member of a custom type, the copy constructor generated by default calls the copy constructor of the member of the custom type to copy.

The copy function generated by default is not omnipotent, such as the stack structure. Initializing st2 with st1 will result in members of both_ a points to the same space.

 

4. Operator overloading

Operator overloading is a powerful tool in C + +, so that objects can also use various operators such as addition, subtraction, multiplication and division to add, subtract, compare size and other meaningful operations. By default, C + + does not support custom types to use operators like built-in type variables. The rules here need to be defined by developers through operator overloading functions.

4.1 definition of operator overloading

Operator overloading enhances the readability and convenience of the code, but we must write operator overloading functions for class objects to achieve this operation. Operator overloading is a function with a special function name, as well as a return type, function name, and parameter list. After the overloaded function is implemented, it is automatically recognized and called by the compiler.

  1. The function name is the keyword operator plus the operator symbols that need to be overloaded, such as operator +, operator =, etc.
  2. The return type and parameters should be determined according to the rules of the operator and the actual situation of the meaning.
bool operator>(const Date& d1, const Date& d2) {
	if (d1._year > d2._year) {
		return true;
	}
	else if (d1._year == d2._year && d1._month > d2._month) {
		return true;
	}
	else if (d1._year == d2._year && d1._month == d2._month && d1._day > d2._day) {
		return true;
	}
	return false;
}
d1 > d2;
operator>(d1, d2);

The size of the two date classes is compared. The parameters are passed in the form of frequent reference of the object to avoid calling the copy constructor and changing the arguments. The return type is Boolean, which is also practical. The compiler converts operator > (D1, D2) into D1 > D2, which greatly improves the readability of the code.

4.2 characteristics of operator overloading

  • Only existing operators can be overloaded, and new operations cannot be defined by connecting other symbols, such as operator @.
  • Overloaded operator functions can only act on user-defined type objects and have at most two parameters. It is best to pass parameters by constant reference for user-defined types.
  • Overload the operator of built-in type. It is recommended not to change the meaning of the operator itself.
  • There are 5 operators that cannot be overloaded, namely:. *, Domain access operator::, sizeof, ternary operator?:, Structure member accessor.

Unlike constructors, operator overloading is a special member function fixed in a class. Operator overloading is applicable to all custom type objects and is not limited to a class alone. However, since the member variables in the class are private, there are three solutions when the operator overload wants them to act on a class:

  1. Modifying the access rights of member variables to become public, but it destroys the encapsulation of the class, which is the most undesirable. Using friend functions, which are similar in nature to modifying access rights, is also undesirable.
  2. The Getter Setter method is used to provide the interface of member variables, which retains encapsulation but is troublesome.
  3. Put the operator overloaded function into the class to become a member function, but you need to pay attention to some details. As an overloaded function of a class member, the formal parameter list hides the this pointer by default, so a reference parameter must be removed.
class Date {
public:
	Date(int year = 0, int month = 1, int day = 1);
	bool operator>(const Date& d);
private:
	int _year;
	int _month;
	int _day;
};
//bool Date::operator>(Date* this, const Date& d) {...}
bool Date::operator>(const Date& d) {
	// ...
}
d1 > d2;
d1.operator>(d2); //Member functions can only be called in this way

4.3 overload of assignment operator

The assignment operator overload implements the assignment of two user-defined types of objects. Unlike the copy constructor, the copy constructor initializes an object with an existing object. The assignment operator overload is the assignment operation of two existing objects. It has the same meaning as the assignment of two integer data, so it is also defined with reference to the assignment operation of built-in type.

  1. Parameter list - two objects are assigned. Since they are placed in a class as member functions, the parameter list only explicitly defines the reference of one object.
  2. Return type - the return value of the assignment expression is also the value of the operand, just return the reference of the object.
// i = j = k = 1;
Date& Date::operator=(const Date& d) {
	if (this != &d) { //Optimize yourself and assign values to yourself
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	return *this;
}

The copy constructor will be called for references that do not pass parameters or do not return objects. In order to reduce copying and avoid modifying the original object, it is best to use constant references.

Default assignment overload assignment rule

If the assignment overloaded function is not explicitly defined in the class, the compiler will generate a member function of the assignment overloaded function by default in the class. The default assignment overload copies the members of built-in types in the form of shallow copy. For members of user-defined types, its internal assignment overload function will be called for assignment.

Therefore, whether to write the assignment overload or not still depends on the situation.

Date d5 = d1;
// Initializing a new object with an existing object is a copy construct rather than an assignment overload

 

Master the above four default functions in C + +, and you can realize the complete date class.

5. Implementation of date class

5.1 definition of date class

class Date {
public:
	Date(int year = 0, int month = 1, int day = 1);
	Date(const Date& d);
	~Date();
    void Print();
	int GetMonthDay();

    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=(const Date& d);

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

	Date& operator++();
	Date operator++(int);
	Date& operator--();
	Date operator--(int);
private:
	int _year;
	int _month;
	int _day;
};

The date class is very simple. The same functions and variables are encapsulated to put the previously associated code together. Next is the specific implementation details of the function interface.

5.2 interface implementation of date class

//Constructor
Date(int year = 0, int month = 1, int day = 1);
//Print
void Print();
//copy construction 
Date(const Date& d);
//Destructor
~Date();
//Get the number of days in the current month
int GetMonthDay();
// >Operator overloading
bool operator>(const Date& d);
// >=Operator overloading
bool operator>=(const Date& d);
// < operator overload
bool operator<(const Date& d);
// < = operator overload
bool operator<=(const Date& d);
// ==Operator overloading
bool operator==(const Date& d);
// != Operator overloading 
bool operator!=(const Date& d);
// =Operator overloading
Date& operator=(const Date& d);
//Date + days = date
Date& operator+=(int day);
//Date + days = date
Date operator+(int day);
//Date - days = date
Date& operator-=(int day);
//Date - date = days  
int operator-(const Date& d);
//Date - days = date
Date operator-(int day);
//Front++
Date& operator++();
//Postposition++
Date operator++(int);
//Front--
Date& operator--();
//Post--
Date operator--(int);

It can also be seen from the above list of function declarations that constructors and destructors are relatively simple, and the key and difficult point of implementing classes is to define overloads of various operators.

Constructor for date class

The constructor of the date class has been implemented before, but we still need to pay attention to some details, such as filtering out some illegal dates. To achieve this function, we need to set the maximum legal days of each month every year, which can be stored in the array MonthDayArray and encapsulated in the function GetMonthDay for calling when judging.

//Gets the maximum number of legal days
int Date::GetMonthDay() {
	static int MonthDayArray[13] = { 0, 31 ,28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	int day = MonthDayArray[_month];
	//Judge leap year
	if (_month == 2 && ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0))) {
		day += 1;
	}
	return day;
}
//Constructor
Date::Date(int year, int month, int day) {
	_year = year;
	_month = month;
	_day = day;
	//Judge whether the date is legal
	if (month > 12 || day > GetMonthDay()) {
		cout << "Please check whether the date is legal:";
		Print();
	}
}

The definition of array MonthDayArray also pays attention to. 13 array elements are defined, and the first element is put to 0, which makes the array subscript correspond to the month and easier to use. Determining the number of days per month also depends on whether the year is a leap year, so it is also necessary to judge whether it is a leap year, because there is one more day in February of a leap year. These packages are in function GetMonthDay. When they are called, they return to the specific days of the month and put them in the constructor to determine whether the date is legal.

Since both functions are defined in the class, the pointer of the class object is used as the function parameter by default, which is more convenient when calling.

Destructors, print functions and copy constructors are very simple. As before, they are not written here. The next step is the implementation of operator overloading.

Overloading of comparison operators
//Operator overloading >
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;
}
//Operator overloading >=
bool Date::operator>=(const Date & d) {
	return (*this > d) || (*this == d);
}
//Operator overloading<
bool Date::operator<(const Date& d) {
	return !(*this >= d);
}
//Operator overloading<=
bool Date::operator<=(const Date& d) {
	return !(*this > d);
}
//Operator overloading==
bool Date::operator==(const Date& d) {
	return (_year == d._year) && (_month == d._month) && (_day == d._day);
}
//Operator overload=
bool Date::operator!=(const Date& d) {
	return !(*this == d);
}

The overloading of comparison operators is not difficult to implement. Just pay attention to the logic of the code. It mainly implements the overloads of > and = = and calls these two functions for others. This implementation method is basically applicable to all classes.

Overload of addition operator

The significance of addition is to realize the operation of date + days = date. You can calculate it on the manuscript and explore the law.

It can be seen that the law of addition is to add the number of days to the number of days, and then judge whether the number of days is legal.

  1. If it is illegal to subtract the maximum legal day value of the current month, it is equivalent to moving to the next month, that is, impairment first and then carry.
  2. If the number of days is legal, the carry operation ends.
  3. While carrying the number of days, if the number of months is equal to 13, it is assigned as 1, and then the year plus 1 to synchronize the remaining days to next year.

The reason for devaluation before rounding is that the devaluation reduces the maximum legal days of the current month. If you carry first, if you modify the month, it will be reduced to the days of the next month.

//Operator overloading+=
//Date + days = date 
Date& Date::operator+=(int day) {
	_day += day;
	//Check whether the number of days is legal
	while (_day > GetMonthDay()) {
		_day -= GetMonthDay();//Days minus legal maximum value --- devaluation first, then carry
		_month++;//Month carry
		//Check whether the number of months is legal
        if (_month == 13) { 
			_month = 1;
			_year += 1;//Year carry
		}
	}
	return *this;
}

Such an implementation method will change the value of the object. It is better to directly implement it as + =, and return the reference of the object to avoid calling the copy construct.

Realize + overload and then de reuse + =.

//Operator overloading+
Date Date::operator+(int day) {//Temporary variables will be destroyed and cannot be passed to reference
	Date ret(*this);
	ret += day; // ret.operator+=(day);
	return ret;
}

Create a temporary variable and initialize with * this, then use the temporary variable for + = operation, and return the temporary variable. Note that the temporary variable is destroyed with the stack frame and its reference cannot be returned.

Overload of subtraction operator
//Operator overloading-=
//Date - days = date
Date& Date::operator-=(int day) {
    //The number of days to prevent is negative
	if (_day < 0) {
		return *this += -day;
	}
	_day -= day;
	//Check whether the number of days is legal
	while (_day <= 0) {
		_month--;//Month borrow
		//Check whether the month is legal
		if (_month == 0) {
			_month = 12;
			_year--;//Year borrow
		}
		_day += GetMonthDay();//Days plus legal maximum - borrow first and then add value
	}
	return *this;
}

The implementation of subtraction logic is similar to addition. First reduce the number of days to the number of days, and then check whether the number of days is legal:

  1. If the number of days is illegal, borrow from the month and add the maximum number of legal days of the previous month, that is, borrow first and then add value. And check whether the month is legal. If the month is 0, it will be set to 12 years for borrowing.
  2. If the number of days is legal, stop borrowing.

First borrow and then add value because the added value is equivalent to removing the days of the previous month, so the days of the previous month should be added.

It should be noted that the operation of correcting the number of months must be placed in front of the value added, because when the number of months is borrowed to 0, it must be corrected to add value normally.

//Operator overloading-
//Date - days = date
Date Date::operator-(int day) {
	Date ret(*this);
	ret -= day;
	return ret;
}
//Date - date = days  
int Date::operator-(const Date& d) {
	int flag = 1;
	Date max = *this;
	Date min = d;
	if (max < min) {
		max = d;
		min = *this;
		flag = -1;
	}
	int gap = 0;
	while ((min + gap) != max) {
		gap++;
	}
	return gap * flag;
}

The calculation of date date = days can be slightly transformed into date + days = date. Let the small date add a value that increases step by step, and the result is equal to the large date, then this value is the difference between the two.

When adding, the date is illegal because the number of days has exceeded the maximum legal days of the current month. Since it has exceeded, the excess part will be left and the maximum legal days of the current month will be subtracted to increase the number of months. Similarly, when subtracting, the date is illegal because the number of days is lower than 0. If you go back to the previous month, fill in the maximum legal value of the previous month and add this negative number. This negative number is equivalent to the remaining days of the month.

Self increasing and self decreasing heavy load

In order to distinguish between pre and post, C + + specifies that the post self increasing and self decreasing overloaded function parameters should be explicitly passed an int parameter placeholder, which can form an overload with the pre.

//Front++
Date& Date::operator++() {
	return *this += 1;
}
//Postposition++
Date Date::operator++(int) {
	return (*this += 1) - 1;
}
//Front--
Date& Date::operator--() {
	return *this -= 1;
}
//Post--
Date Date::operator--(int) {
	return (*this -= 1) + 1;
}
// Implementation mode 2
Date ret = *this;
*this + 1;
return ret;

To realize the self increment and self decrement of the pre and post objects, it is necessary to meet the characteristics of pre operation before use and post operation before use. You can also use the overload reuse realized above. Alternatively, you can directly save * this by using the temporary variable, and return to the temporary variable after changing * this.

++d2;
d2.operator();
d1++;
d1.operator(0);

It can be seen that for class objects, pre + + is much faster than post + + and only calls the destructor once, while post + + calls two copy constructions and three destructors.

 

6. const class

The class modified by const is const class. An error occurs when const class calls a member function, because the parameter this pointer from const Date * to Date * involves permission amplification. As shown in the figure:

6.1 member function of const class

To avoid this problem, we must modify the formal parameter this of the member function, but the this pointer cannot be explicitly used as a parameter, so it cannot be modified. To solve this problem, C + + stipulates adding const after the function declaration, which is equivalent to adding const modification to the formal parameter this pointer.

//Operator overload=
//statement
bool Date::operator!=(const Date& d) const;
//definition
bool Date::operator!=(const Date& d) const {
	return !(*this == d);
}

Like the above code, the class member function modified by const is called const member function. Const modifies the class member function and actually modifies the implicit parameter this pointer of the function, so that the function cannot modify the member variable of the object.

6.2 overload of address fetching operator

There are also two default member functions of the class, the overload of the address taking operator and the overload of the const address taking operator. These two default member functions generally do not need to be defined, and the default generated by the compiler is enough.

Date* operator&() {
    return this;
    //return NULL; // Getting the address of an object is not allowed
}
const Date* operator&() const {
    return this;
}

When it is not allowed to obtain the address of the object, you can overload the address to be empty.

Keywords: C++ Back-end

Added by JAM on Tue, 25 Jan 2022 20:08:52 +0200