C + + learning notes -- classes and objects (medium)

catalogue

1. Six default member functions of the class

2. Constructor

2.1 concept

2.2 characteristics

3. Destructor

3.1 concept

3.2 characteristics

4. Copy constructor

4.1 concept

4.2 characteristics

5. Overload of assignment operator

5.1 operator overloading

5.1 overload of assignment operator

6. const members

6.1 member function of const modifier class

6.2. Overload of address fetching and const address fetching operators

 7. Implementation of date class (you can practice using the above knowledge)

1. Six default member functions of the class

If there are no members in a class, it is called empty class for short. There is nothing in empty class? No, any class will automatically generate the following six default member functions without writing.

class Date{};

2. Constructor

2.1 concept

Concept: the constructor is a special member function with the same name as the class name. When creating a class type object, it is automatically called by the compiler

With, ensure that each data member has an appropriate initial value and is called only once in the life cycle of the object.

2.2 characteristics

Constructors are special member functions. It should be noted that although the name of constructors is called constructors, it should be noted that the main task of constructors is not to open space to create objects, but to initialize objects.

Its characteristics are as follows:

  • The function name is the same as the class name
  • No return value
  • The object instantiation compiler automatically calls the corresponding constructor
  • Constructors can be overloaded
class Date
{
public:
	// Constructor
	// 1. Parameterless constructor
	Date()
	{

	}
	// 2. Parametric structure
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 3. Full default
	//Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    //
private:
	int _year;
	int _month;
	int _day;
};
void test(){
  Date d1;//Call parameterless constructor
  Date d2(2022,1,18);//Call the constructor with parameters
}

Note: if the defined constructor is not displayed in the class, the C + + compiler will automatically generate a parameterless default constructor. Once the user displays the defined constructor, the compiler will no longer generate it automatically

There are three default constructors:

  • The constructor generated by the compiler by default (the built-in types will not be processed, or random values; for custom types, they will call their own constructor initialization)
  • Full default constructor
  • non-parameter constructor

When we do not implement the constructor, the compiler will call the constructor generated by the compiler by default. Look at the following code. Can the compiler's default constructor initialize member variables?  

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


int main()
{
	Date  d1;
	d1.Print();

	return 0;
}

 
 

All three values are random values. The default constructor of the compiler does not initialize the member variables. Does that mean that this function is useless?

Answer: C + + divides types into built-in types (basic types) and custom types. A built-in type is a type whose syntax has already been defined: for example, int/char, A custom type is a type defined by ourselves using class/struct/union. If you look at the following program, you will find that the compiler will generate a default constructor for custom type members_ t calls its default member function

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
			_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// Basic type (built-in type)
	int _year;
	int _month;
	int _day;
	// Custom type
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

Summary: 1 The default constructor built-in type (int/char/double) will not be processed, but it is still a random value; For custom types (struct/class), they will call their own constructor initialization.

If there is no explicit constructor defined by the user in. 2 + +, the compiler will no longer generate a default constructor automatically

3. In general, it is recommended to write a fully default constructor, which can adapt to most scenarios.

3. Destructor

3.1 concept

Concept: Contrary to the constructor function, the destructor does not complete the destruction of objects, and the destruction of local objects is completed by the compiler. When the object is destroyed, it will automatically call the destructor to clean up some resources of the class

3.2 characteristics

Destructors are special member functions

Its characteristics are as follows:

  • The destructor name is preceded by a character in the class name~
  • No parameter, no return value
  • A class has only one destructor If no definition is displayed, the system will automatically generate a destructor.
  • When the object declaration cycle ends, the C + + compilation system will automatically call the destructor.
typedef int DataType;
class SeqList
{
public :
SeqList (int capacity = 10)
{
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
}
~SeqList()//Destructor
{
if (_pData)
{
free(_pData ); // Free up space on the heap
_pData = NULL; // Set pointer to null
_capacity = 0;
_size = 0;
}
}
private :
int* _pData ;
size_t _size;
size_t _capacity;
};
class String
{
public:
String(const char* str = "jack")
{
_str = (char*)malloc(strlen(str) + 1);
strcpy(_str, str);
}
~String()
{
cout << "~String()" << endl;
free(_str);
}
private:
char* _str;
};
class Person
{
private:
String _name;
int _age;
};
int main()
{
Person p;
return 0;
}
//Similarly, for the default destructor: the built-in type member is not processed, and its destructor will be called for the user-defined type

If we do not implement the destructor, the compiler will automatically generate a destructor, and the built-in type of the destructor heap will not be processed. Its destructor will be called for custom types, which is somewhat similar to the default constructor generated by the compiler.

class A
{
public:
	A()
	{
		cout << "A()" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

class B
{
public:
	B()
	{
		cout << "B()" << endl;
	}

	~B()
	{
		cout << "~B()" << endl;
	}
private:
	int _b;
};

int main()
{
	A a;
	B b;

	return 0;
}
// Because the object is defined in the function, the function call will establish the stack frame,
	// The object construction and Deconstruction in the stack frame should also be very consistent with last in first out.
	// So the order here is: a () - > b () - > ~ B () - > ~ a ()

 

4. Copy constructor

4.1 concept

When creating an object, can you create a new object that is the same as an object?
Constructor: there is only a single parameter, which is a reference to the object of this class type (commonly used const decoration). It is automatically called by the compiler when creating a new object with an existing class type object

4.2 characteristics

  • Copy constructor is an overloaded form of constructor;

  • There is only one parameter of the copy constructor, and the parameter must be passed by reference. Using the value passing method will lead to infinite recursive calls;

class Date {
public:
	Date(int year = 2020, int month = 2, int day = 20)
	{
		_year = year;
		_month = month;
		_day = day;
	}
  // Date(Date d)
//If value passing copy is used, the copy constructor will be called first when passing parameters (because the original data needs to be copied to the temporary space before using the data in the temporary space for assignment). In this way, copy will occur again, which will fall into a circular process.   
   {
      _year=d._year;
      _month=d._month;
      _day=d._day;
     }//
	Date(const Date& d)//It is also recommended to add const when passing parameters here, because the copied object should not be modified
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void print()
	{
		cout << _year << "-" << _month << "-" << _day  << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
// Summary:
// 1. A class like date needs shallow copy, so the copy structure generated by default is enough, and you don't need to write it yourself
// 2. However, a class like Stack needs a deep copy. A shallow copy will lead to two destructions and the program will crash

Causes of infinite recursion caused by value passing:

  • 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.

For a custom type (Stack, etc.), the compiler will still make a shallow copy of it, which will copy the address of the space opened up by the original object. The consequences are as follows:
1. Two objects share a space, which will be released twice when calling the destructor;
2. Inserting and deleting data for one object will cause the other object to insert and delete data.
Therefore, for classes such as Stack, the copy structure generated by the compiler by default is a shallow copy, which does not meet the requirements and needs to implement a deep copy, which will not be introduced here and will be analyzed in later chapters

class String
{
public:
	String(const char* str = "jack")
	{
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~String()
	{
		cout << "~String()" << endl;
		free(_str);
	}
private:
	char* _str;
};
int main()
{
	String s1("hello");
	String s2(s1);
}

The code crashes directly, which is the consequence of shallow copy: the same memory is destructed twice

5. Overload of assignment operator

5.1 operator overloading

In order to enhance the readability of the code, C + + introduces operator overloading. Operator overloading is a function with special function name, and also has its return value type, function name and parameter list. Its return value type and parameter list are similar to ordinary functions.
The function name is: the keyword operator is followed by the operator symbol that needs to be overloaded.
Function prototype: return value type operator operator (parameter list)

be careful:

  • You cannot create a new operator by concatenating other symbols: for example, operator@
  • Overloaded operators must have an operand of class type or enumeration type
  • The meaning of operators used for built-in types cannot be changed. For example, the meaning of built-in integer +
  • When an overloaded function is a member of a class, its formal parameters appear to be 1 less than the number of operands
    The operator has a default parameter this, which is limited to the first parameter
  • * ,:: ,sizeof ,?: ,. Note that the above five operators cannot be overloaded. This often appears in written multiple-choice questions.
     

Code example: '>'

class Date
{
public:
    Date(int year = 2022, int month = 2, int day = 20)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    
    // bool operator>(Date* this, const Date& d2)
 
    bool operator>(const Date& d2)
    {
        
    if (_year > d2._year)
	{
		return true;
	}
	else  if(_year == d2._year && _month > d2._month)
	{
		return true;
	}
	else if (_year == d2._year && _month == d2._month && _day > d2._day)
	{
		return true;
	}
	else {
		return false;
	}
    }
private:
    int _year;
    int _month;
    int _day;
};
void Test ()
{
    Date d1(2021, 1, 1);
    Date d2(2021, 1, 2);
    cout<<(d1 >d2)<<endl;// < < priority is higher than = =, parentheses are required here
    //Equal to D1 operator>(d2)
}

5.1 overload of assignment operator

There are five main assignment operators:

  • Parameter type
  • Return value
  • Check whether you assign values to yourself
  • Return * this
  • If a class does not explicitly define an assignment operator overload, the compiler will also generate one to complete the byte order value copy of the object.
class Date
{
public:
	Date(int year = 2000, int month = 9, int day = 18)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// The overload of assignment operator is also a default member function, that is, the compiler will automatically generate it if we don't write
	// The overloaded assignment operator generated by the compiler by default has the same characteristics as the copy construct
	// The built-in type will complete the shallow copy, and the user-defined type will call its assignment operator overload to complete the copy
	Date& operator=(const Date& d) // The return value is used to solve the problem of continuous assignment
	{
		if (this != &d)          //You don't need to copy until you assign a value to yourself
		{
			_year = d._year;            // The overloading of assignment operators is also a copy behavior. The difference is,
			_month = d._month;          // Copy construction is a copy that initializes similar objects when creating an object.
			_day = d._day;              // When the assignment copy here, both objects already exist and have been initialized
		}
		return *this;
	}
 
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
 
private:
	int _year;
	int _month;
	int _day;
};
 
 
int main()
{
	Date d1;
    Date d2(2022,2,20);
    d1=d2;//Here, the compiler called by d1 generates operator = to complete the copy, and the values of d2 and d1 are the same
 
	Date d3;
	d3 = d2 = d1;//continuous assignment 
	d2.Print();
	d3.Print();
 
	Date d4 = d1; // copy construction 
	return 0;     // Copy construction: copy an existing object and initialize another object to be created
}                 // Assignment overload: two existing object copies
            

The difference between overload of assignment operator and copy constructor

Same: when there is no definition displayed, the compiler will generate one by default. For built-in types, the shallow copy of byte order will be carried out, and for custom types, its own copy constructor or operator = will be called.
Difference: copy construction: initialize an object to be created with an existing object.
Assignment overload: two copies of existing objects.

6. const members

6.1 member function of const modifier class

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

class Date
{
public :
void Display () const
{
cout<<"Display () const" <<endl;
cout<<"year:" <<_year<< endl;
cout<<"month:" <<_month<< endl;
cout<<"day:" <<_day<< endl<<endl;
}
private :
int _year ; // year
int _month ; // month
int _day ; // day
};
void Test ()
{
Date d1 ;
d1.Display ();//Non const can call const; Const cannot call non const (permission cannot be enlarged)
const Date d2;//It is recommended that const be added to member functions without changing member variables.
d2.Display ();
}

6.2. Overload of address fetching and const address fetching operators

These two operators generally do not need to be overloaded. They can be overloaded by using the default address generated by the compiler. Only in special cases, they need to be overloaded, for example, if you want others to get the specified content.

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

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

 7. Implementation of date class (you can practice using the above knowledge)

Date.h

Date.h
#pragma once
#include<iostream>
using namespace std;
class Date {
public:
	Date(int year = 1, int month = 1, int day = 1);
	void print();
	int GetMonthDay(int year,int  month);//Gets the number of days per month
	bool operator>(const Date& d);//Compare whether the first number is larger than the second
	bool operator<(const Date& d);//The same is true below
	bool operator>=(const Date& d);
	bool operator<=(const Date& d);
	bool operator==(const Date& d);
	bool operator!=(const Date& d);
	//d1+=100
	Date& operator+=(int day);//Adding days to a number changes itself
	//d1+100
	Date operator+(int day);//What day is a number plus a given number of days? Itself does not change
	//d1-=100
	Date& operator-=(int day);
    //d1-100
	Date operator-(int day);
	Date operator--(int);
	Date& operator--();
	Date operator++(int);//For post + +, in order to distinguish between pre + + and its constituent function overload, you need to add int (specified, other types are not allowed)
	Date& operator++();//Front++
	int operator-(const Date& d);//Subtract two dates
	void PrintWeekDay() const;//Print the day of the week today

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

Date.c 

#include"Date.h"
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	if(!(_year >= 0 && (_month > 0 && _month < 13) && (_day>0 && _day <= GetMonthDay(year, month))))
	{
		cout << "Illegal date" << endl;
	}
}
void Date::print()
{
	cout << _year << "-" << _month << "-" << _day << endl; 
}
int Date::GetMonthDay(int year, int month)
{
	int MonthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = MonthDayArray[month];
	if (month == 2 && (year % 4 == 0 && year % 400 != 0) || year % 100 == 0)
	{
		day += 1;
	}
	return day;
}
bool Date::operator>(const Date& d)
{
	if (_year > d._year)
	{
		return true;
	}
	else  if(_year == d._year && _month > d._month)
	{
		return true;
	}
	else if (_year == d._year && _month == d._month && _day > d._day)
	{
		return true;
	}
	else {
		return false;
	}
}
bool Date::operator==(const Date& d)
{
	if (_year == d._year && _month == d._month && _day == d._day)
	{
		return true;
	}
	else {
		return false;
	}
}
bool Date::operator>=(const Date& d)
{
	return (*this > d || *this == d);
}
bool Date::operator<(const Date& d)
{
	return !(*this >= d);
}
bool Date::operator<=(const Date& d)
{
	return !(*this > d);
}
bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}
Date& Date::operator+=(int day)
{
	//2021-12-28 +=300
	_day += day;
	while ( _day> GetMonthDay(_year, _month))
	{
		_month += 1;
		_day -= GetMonthDay(_year, _month);
		while (_month == 13)
		{
			_year += 1;
			_month = 1;
		}
	}
	return *this;
}
Date Date::operator+(int day)
{
	Date ret(*this);
	ret += day;
	return ret;
}
Date& Date::operator-=(int day)
{
	//2021-1-20 -45
	_day -= day;
   while(_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
   return *this;
}
Date Date::operator - (int day)
{
	Date ret(*this);
	ret -= day;
	return ret;
}
//Front++
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
Date Date::operator++(int)
{
	Date ret(*this);
	*this += 1;
	return ret;
}
Date& Date::operator--()
{
	*this -= 1;
	return *this;
}
Date Date::operator--(int)
{
	Date ret(*this);
	*this -= 1;
	return ret;
}
int Date::operator-(const Date& d)
{
	Date max = *this;
     Date min = d;
	 int flag = 1;
	 if (*this < d)
	 {
		 max = d;
		 min = *this;
		 flag = -1;
	 }
	 int count = 0;
	 while (min != max)
	 {
		 min++;
		 count++;
	 }
	 return flag * count;
}
void Date::PrintWeekDay() const
{
	const char* arr[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
	Date start(1900, 1, 1);
	int count = *this - start;
	cout << arr[count % 7] << endl;
}

The above is part of the content of classes and objects. If there are deficiencies or have better opinions on the code, please leave a message in the comment area to discuss and make progress together!!  

 

Keywords: C++

Added by s2day on Sun, 20 Feb 2022 12:01:59 +0200