C + + classes and objects (primary level): class definition, size, six default member functions and this pointer. 10000 word summary!!!!!!

Class definition:  

Class is the keyword defining the class, ClassName is the name of the class, and {} is the body of the class. Note that there is also a semicolon after the end of class definition. The elements in a class are called class members: the data in a class is called class attributes or member variables; Functions in a class are called methods or member functions of the class.

The C language structure does not support member functions, but the C + + structure supports them. There is no difference between class and struct in essence, but the only difference    By default, the access property of class is private and struct is public

Access qualifier for class:

Members decorated with public can be accessed directly outside the class
Protected and private modified members cannot be accessed directly outside the class (protected and private are similar here)
The scope of access rights starts from where the access qualifier appears until the next access qualifier appears

The base class protects members that can be accessed directly in subclasses

Private members of a base class cannot be accessed in subclasses

Common members of the base class can be accessed directly outside subclasses and objects

There are three methods to define a member function of a class:

//Methods are always implemented within classes
class englishbook {
public:
    void initcount(int n) {
        count = n;
    }
    int getcount() {
        return count;
    }
private:
    int count;
    char name;
};
//Method 2: declaration within the class, implementation outside the class, and scope restriction
class englishbook {
public:
    void initcount(int n);
    int getcount();
private:
    int count;
    char name;
};
void englishbook::initcount(int n) {
    count = n;
}
int englishbook::getcount() {
    return count;
}
int main()
{
    englishbook book1;//An englishbook class object book1 is instantiated
    book1.initcount(3);
    cout << "book1count:" << book1.getcount() << endl;
    return 0;
}

The third method:

  The main function is placed in another source file

Size of class object:

Members occupy the space of the object. The size of a class is only determined by the member variable. It is the same as the memory alignment rule of the structure. Empty classes occupy one byte

Class methods occupy system resource space (common code area). In order to prevent calling the same method when a class has multiple instances, it creates space for multiple methods of the same size

this pointer:

  If a class defines two instantiated objects, such as Englishbook book1 and Englishbook Book2, how does the function know that the book1 object should be set instead of the s2 object when the intitcount function is called during book1 initialization?

The C + + compiler adds a hidden pointer parameter to each "non static member function" so that the pointer points to the current object (the object that calls the function when the function runs). All member variable operations in the function body are accessed through the pointer. However, all operations are transparent to the user, that is, the user does not need to pass them, and the compiler completes them automatically.

  So: this means the current object (which instantiated object calls the function, and this refers to the object at this time)

class englishbook {
public:
    /*It should be noted that the view can be regarded as the following, but when writing formal parameters, you can't write the this pointer yourself like this

    void initcount(englishbook* const this,int n){
        this->count = n;

    }*/
   
    void initcount(int n) {
        count = n;
    }

    //But you can call this yourself
     void initcount(int n) {
        this->count = n;
    }
private:
    int count;
    char name;
};
int main()
{
    englishbook book1;//An englishbook class object book1 is instantiated
    englishbook book2;
    book1.initcount(3);
    book2.initcount(4);
    //It can be understood as book1.initcount (& book1,3); book2.initcount (& book2,3)   
    //In this way, the function will know which object is the member of the operation
    return 0;
}

Moreover, the this pointer is a constant (defined as class name * const this), so the this pointer cannot be modified in the scope of the whole member function, let alone empty (this=nullptr)

Other parameters of member functions are normally stored in the stack, while the this pointer parameter is stored in the register

When this pointer is not required:

The formal parameter name and member variable name are the same

 void initcount(int count) {
        count = count;
    }

If you do this directly, it is equivalent to that count copies itself without getting the value of the argument

  void initcount(int count) {
        this->count = count;
    }

Only in this way can count be initialized

Class's six default member functions:

        Constructor:

The constructor has no return value, the function name is the same as the class name, and can have multiple parameters. If you do not declare and implement the constructor yourself, you will automatically call an empty default constructor that does nothing. However, if you declare and implement a constructor yourself, you will not call an empty default constructor  

Constructors can be overloaded, but two or more default constructors are not allowed in a class (constructors without parameters or all default parameters are called default constructors), because instantiating a test t1 without parameters will cause ambiguity errors. As follows:

class test {
public:
    test() {
        _a = 10;
    }
    test(int d = 0) {
        _a = d;
    }
private:
    int _a;
};
int main(){
    test t1;
}

Therefore, we generally write only one constructor with full default parameters by default. At this time, when instantiating an object, test t1;test t2(10) is in parentheses. If it is not an object after the equal sign, it is equivalent to test t2=10. These three instantiated objects can be given, but it is impossible to give parameters () without arguments in parentheses. Therefore, we do not write test t3() by default This kind of object without arguments causes t3 to become a function with the return value type of test instead of instantiating t3. It is as follows:

class test {
public:
    test(int d = 0) {
        _a = d;
    }
private:
    int _a;
};
int main(){
    test t1;//ok
    test t2(100);//Equivalent to test t2=10;ok
    test t3();//no ok, this is a function
}

         Destructor:

The destructor also has no return value. The function name is the class name preceded by an inverse sign ~, which is not allowed to be overloaded

A class has only one destructor. If it is not explicitly defined, the system will automatically generate the default destructor. At the end of the object life cycle, the C + + compilation system will automatically call the destructor     The destructor does not complete the destruction of the object. The destruction of local objects is completed by the compiler. When the object is destroyed, the destructor will be called automatically to clean up some resources of the class.

Order of calling destruct: first instantiate and construct the global object, then instantiate the object from top to bottom in the main function, and then destruct the object from bottom to top, except for the static of the main function. The static object in the main function is destructed last (static is constructed first and then destructed). As follows:  

        Copy constructor:

When creating a new object with an existing class type object (three cases: when creating a new object directly from the old object, when the object initializes a non referenced formal parameter object as an argument, the copy constructor will also be called when the local variable in the function is used as the return value, which will be copied to the nameless temporary space) the compiler will automatically call the copy constructor.

1. Copy constructor is an overloaded form of constructor
2. There is only one parameter of the copy constructor, and the reference must be used to pass the parameter (and it is generally modified with const to prevent changing the value of the original object). The reference is the alias of the original object and will not create a new space. Using the value passing method will cause infinite recursive call, because the assignment of the actual parameter d1 to the formal parameter date is also an object, and the initialization object will call the copy constructor (Date date=d1) , and then recurs:

 

3. If the definition is not displayed, the system generates the default copy constructor. The default copy constructor object completes the copy in byte order according to memory storage. This kind of copy is called shallow copy or value copy. However, some classes will crash if they are shallow copies. In this case, it is necessary to use the subsequent deep copy

        Operator overloaded function:

. *,::, sizeof,?:,. Note that the above five operators cannot be overloaded. This often occurs in written multiple-choice questions.

Take the overload of the assignment operator (i.e. the equal sign "=") as an example to understand the situation that the operator overload function will be called:

test t1;

t1=100;//The compiler will first call the constructor with constant 100 to construct an unnamed test class, and then call the assignment overload function to assign a value to t1,
//t1.operator=(test(100));

test t2;

t2=t1;//Conventional call assignment overloaded function
//t2.operator=(t1);

///In fact, operator = is equivalent to the function name fun, and other operator overloads are the same

//t2.fun(t1);  

//It is equivalent to calling a member function named operator =, so the member function definition implements that the explicit formal parameter is t1
//The implicit this pointer points to t2 because it is the operator overloaded function called by t2

Before understanding operator overloading, let's understand:

Problems with parameters and return value types of ordinary functions:

The first method: directly using the class as the shape participation will lead to the call of the copy construction method. When the class returns as the return value type, it will call the copy construction again to copy the tmp to the nameless temporary variable

 

The second: reference as formal parameter obj does not need to copy. The structure is the alias of t1, and the temporary space variable assigns a value to t2

 

  The third type: reference is used as the return value. Since it refers to the alias of tmp and tmp has been released in the life cycle, it is unreasonable to assign it to t2 at this time! (however, if the member function inside the class returns * this, it is returned by reference. Because * this is an instantiated object, the function ends and * this still exists! tmp here is the internal local variable of the function, and its life ends when the function ends!   * This is returned by reference because this is also a formal parameter, which is implicitly defined. After the function ends, the pointer variable of this class will also be released. If you do not return by reference, the copy constructor will be called when returning, and the temporary space will be constructed by * this copy. Returning * this by reference is unnecessary.)

  The fourth method: the return object of fun directly initializes t2 and enters the fun function first. Because the compiler optimization will make the temporary space t2 directly, skip and omit the temporary space variable, and then call the copy structure to t2

(   test t2=fun(t1)   Equivalent to test t2(fun(t1))        ), Belongs to initialization

  Fifth, after the final optimization, the unknown temporary variable is returned directly, and the temporary variable returned by the compiler is t2. The intermediate variable tmp is not created, and the construction of tmp and copy structure are omitted

 

After the above problems, we should understand that the operator overloads the formal parameters of the function, and the return value type depends on the situation:

=Overloaded functions are returned by class reference so that they can be connected. If the return type is void, t3=t2=t1 cannot be used

Namely     t3.operator=(t2.operator=(t1))   For the difference between the reference of the return class and that of the return class, see < the third type: >

+=Operator overloading operates on itself, so we choose to return ourselves (Reference)

+Operator overloading, we want to get a new date class without changing itself, so we don't return a reference

#include <iostream>
class Date{
public:
	// Gets the number of days in a month of a year
	int GetMonthDay(int year, int month){
		int days[13] = { 0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		int day = days[month];
		if (month == 2&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))){
			++day;
		}
		return day;
	}
	// Full default constructor
	Date(int year = 1900, int month = 1, int day = 1){
		_year = year;
		_month = month;
		_day = day;
	}
	// Copy constructor, d2(d1)
	Date(const Date& d){
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	// Overload of assignment operator, D2 = D3 - > D2. Operator = (& D2, D3)
    //Return reference
	Date& operator=(const Date& d){
			//You can only assign values if you do not assign values to yourself
		if (*this != d) {
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
	// Destructor
	~Date(){}
	// Date + = days
	Date& operator+=(int day){
		if (_month!=12) {
			while (day > 365) {
				_year++;
				day = day - 365;
			}
			if ((_day + day) > this->GetMonthDay(_year, _month)) {
				_day = _day + day;
				while (_day > this->GetMonthDay(_year, _month)) {
					_day -= this->GetMonthDay(_year, _month);
					_month++;
				}
			}
			else {
				_day += day;
			}
		}
		else{
			while (day > 365) {
				_year++;
				day = day - 365;
			}
			if ((_day + day) > 31) {
				_day = _day + day-31;
				_year++;
				_month = 1;
				while (_day > this->GetMonthDay(_year, _month)) {
					_day -= this->GetMonthDay(_year, _month);
					_month++;
				}
			}
			else {
				_day += day;
			}
		}
		return *this;
	}
	// Date + days
	Date operator+(int day){
		int oldyear = _year;
		int oldmonth = _month;
		int oldday = _day;
		if (_month != 12) {
			while (day > 365) {
				_year++;
				day = day - 365;
			}
			if ((_day + day) > this->GetMonthDay(_year, _month)) {
				_day = _day + day;
				while (_day > this->GetMonthDay(_year, _month)) {
					_day -= this->GetMonthDay(_year, _month);
					_month++;
				}
			}
			else {
				_day += day;
			}
		}
		else {
			while (day > 365) {
				_year++;
				day = day - 365;
			}
			if ((_day + day) > 31) {
				_day = _day + day - 31;
				_year++;
				_month = 1;
				while (_day > this->GetMonthDay(_year, _month)) {
					_day -= this->GetMonthDay(_year, _month);
					_month++;
				}
			}
			else {
				_day += day;
			}
		}
		Date tmp = *this;
		_year = oldyear ;
		_month = oldmonth ;
		_day = oldday;
		return tmp;
	}
	// Date - Days
	Date operator-(int day){
		int oldyear = _year;
		int oldmonth = _month;
		int oldday = _day;
		if (_month != 1) {
			while (day > 365) {
				_year--;
				day -= 365;
			}
			if (_day - day < 1) {
				_day = _day - day;
				while (_day < 0) {
					_day += this->GetMonthDay(_year, _month);
					_month--;
				}
			}
			else {
				_day -= day;
			}
		}
		else {
			while (day > 365) {
				_year--;
				day -= 365;
			}
			if (_day - day < 1) {
				_day = _day - day + 31;
				_year--;
				_month = 12;
				while (_day < 0) {
					_month--;
					_day += this->GetMonthDay(_year, _month);
				}
			}
			else {
				_day -= day;
			}
		}
		Date tmp = *this;
		_year = oldyear;
		_month = oldmonth;
		_day = oldday;
		return tmp;
	}
	// Date - = days
	Date& operator-=(int day){
		if (_month != 1) {
			while (day>365) {
				_year--;
				day -= 365;
			}
			if (_day-day<1) {
				_day = _day - day;
				while (_day < 0 ) {
					_day += this->GetMonthDay(_year, _month);
					_month--;
				}
			}
			else {
				_day -= day;
			}
		}
		else {
			while (day > 365) {
				_year--;
				day -= 365;
			}
			if (_day - day < 1) {
				_day = _day - day+31;
				_year--;
				_month = 12;
				while (_day < 0) {
					_month--;
					_day += this->GetMonthDay(_year, _month);					
				}
			}
			else {
				_day -= day;
			}
		}
		return *this;
	}
	// Front++
	Date& operator++(){
		++_day;
		return *this;
	}
	// Post++
	Date operator++(int){
		Date tmp=*this;
		_day++;
		return tmp;
	}
	// >Operator overloading
	bool operator>(const Date& d){
		if (_year == d._year) {
			if (_month == d._month) {
				if (_day == d._day) {
					return false;
				}
				else
					return 	_day > d._day ? true : false;
			}
			else 
				return _month > d._month ? true : false;
		}
		else 
			return _year > d._year ? true : false;
	}
	// ==Operator overloading
	bool operator==(const Date& d){
		if (_year == d._year && _month == d._month && _day == d._day)
			return true;
		else
			return false;
	}
	// >=Operator overloading
	inline bool operator >= (const Date& d){
		if (_year == d._year) {
			if (_month == d._month) {
				if (_day == d._day) {
					return true;
				}
				else
					return 	_day > d._day ? true : false;
			}
			else
				return _month > d._month ? true : false;
		}
		else
			return _year > d._year ? true : false;
	}
	// < operator overload
	bool operator < (const Date& d){
		if (_year == d._year) {
			if (_month == d._month) {
				if (_day == d._day) {
					return false;
				}
				else
					return 	_day < d._day ? true : false;
			}
			else
				return _month < d._month ? true : false;
		}
		else
			return _year < d._year ? true : false;
	}
	// < = operator overload
	bool operator <= (const Date& d){
		if (_year == d._year) {
			if (_month == d._month) {
				if (_day == d._day) {
					return true;
				}
				else
					return 	_day < d._day ? true : false;
			}
			else
				return _month < d._month ? true : false;
		}
		else
			return _year < d._year ? true : false;
	}
	// ! = operator overload
	bool operator != (const Date& d){
		if (_year == d._year && _month == d._month && _day == d._day)
			return false;
		else
			return true;
	}
	// Date - date return days
	int operator-( Date& d){
		int n = 0;
		int oldyear = d._year;
		int oldmonth = d._month;
		int oldday = d._day;
		if (*this < d) {
			printf("Incorrect date entered,Please re-enter\n");
			return -1;
		}
		else {	
			while (*this != d) {
				if (_month == d._month&&_year==d._year) {
					n = n+(_day - d._day);
					d._year = oldyear;
					d._month = oldmonth;
					d._day = oldday;
					return n;
				}
				n += d.GetMonthDay(d._year, d._month) - d._day+1;
				d._month++;
				d._day = 1;
			}
		}
	}
private:
	int _year;
	int _month;
	int _day;
};
int main(){
	Date day1(2021,10,3);
	Date day2(2021,11,25);
	int a = day2 - day1;
	return 0;
}

Overload of address fetching and const address fetching operators:

These two are generally regarded as two default member functions other than the above four default functions

These two operators generally do not need to be overloaded. You can use the overloaded default address generated by the compiler. Only in special cases do you need to overload, such as if you want others to get the specified content!

class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
//Equivalent to const date * operator & (const date * this)
{
return this ;
}
private :
int _year ; // year
int _month ; // month
int _day ; // day
};
int main(){

Date t1;
Date* pt1=&t1;//Call first

const Date t2;
const Date* pt2=&t2;//Calling the second, a constant object can only be given to a constant object

return 0;
}

Tags: C++

Posted on Mon, 18 Oct 2021 13:17:56 -0400 by kentlim04