[C++]02_ Classes and objects (medium)

[C++]02_ Classes and objects (medium)

01. Default member function of class

If a class has no members, it is referred to as an empty class. Is there nothing in the empty class? No, any class will automatically generate the following six default member functions without writing.

class Date {};
  1. Initialization – > constructor;
  2. Cleanup – > destructor;
  3. Initialize and create objects using similar objects – > copy constructor;
  4. Assign an object to another object – > assign overloaded function;
  5. Address fetching of common objects – > address fetching overload;
  6. const object get address – > get address overload.

02. Constructor

2.1 concept

//Date class
class Date
{ 
public:
     void SetDate(int year, int month, int day)
     {
         _year = year;
         _month = month;
         _day = day;
     }
 
     void Display()
     {
         cout <<_year<< "-" <<_month << "-"<< _day <<endl;
     }
private:
     int _year;
     int _month;
     int _day;
};
int main()
{
     Date d1,d2;
     d1.SetDate(2018,5,1);
     d1.Display();
 
     Date d2;
     d2.SetDate(2018,7,1);
     d2.Display();
     return 0;
}
//For the Date class, you can set the content of the object through the public method of SetDate. However, if you call this method to set the information every time you create the object, it will be a bit troublesome. Can you set the information when the object is created?
//Then you need to use the constructor!

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 to 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

be careful 🗣: Although the name of the constructor is called construction, it should be noted that the main task of the constructor is not to open space to create objects, but to initialize objects.

Constructor characteristics:

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

  2. No return value;

  3. When an object is instantiated, the compiler automatically calls the corresponding constructor;

  4. Constructors can be overloaded;

  5. If the constructor is not explicitly defined in the class, the C + + compiler will automatically generate a parameterless default constructor. Once the user explicitly defines the constructor, the compiler will no longer generate it;

class Date
{
public:
     /*
     // If the user explicitly defines a constructor, the compiler will no longer generate a constructor
     Date (int year, int month, int day)
     {
     _year = year;
     _month = month;
     _day = day;
     }
     */
private:
     int _year;
     int _month;
     int _day;
};
void Test()
{
 // If there is no constructor defined, the object can be created successfully, so the default constructor generated by the compiler is called here
 Date d;
}
  1. Both parameterless constructors and fully default constructors are called default constructors, and there can only be one default constructor. Note: parameterless constructors, all default constructors, and constructors generated by the compiler by default can be considered as default member functions.

    // Default constructor 
    class Date
    { 
    public:
         Date()
         {
         _year = 1900 ;
         _month = 1 ;
         _day = 1;
         }
    
         Date (int year = 1900, int month = 1, int day = 1)
         {
         _year = year;
         _month = month;
         _day = day;
         }
    private :
         int _year ;
         int _month ;
         int _day ;
    };
    // Can the following test functions be compiled?
    void Test()
    {
         Date d1; 	//❌, Error, "class" Date "contains multiple default constructors", which can verify our conclusion on the default constructor;
    
    }
    
  2. Many children's shoes have doubts about the default member function generated by the compiler: the compiler will generate the default constructor when we do not implement the constructor. But it seems that the default constructor is useless? The d object calls the compiler generated default constructor, but the d object year/month/_day is still a random value. In other words, the default constructor generated by the compiler is useless here??

Answer: C + + divides types into built-in types (basic types) and custom types. The built-in type is the type defined by the syntax, such as int/char... And the custom type is the type defined by class/struct/union. If you look at the following program, you will find that the compiler will generate the default constructor for the 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;
}

8. Naming style of member variables

// Let's see if this function is rigid and difficult to distinguish two year s. We also said before that although this - > can be added to distinguish, it is still not recommended.
class Date
{
public:
     Date(int year)
     {
     // Is the year here a member variable or a function parameter?
     year = year;
     }
private:
     int year;
};
// So we generally recommend this
class Date
{
public:
     Date(int year)
     {
     _year = year;
     }
     private:
     int _year;
};
// Or so.
class Date
{
public:
     Date(int year)
     {
     m_year = year;
     }
private:
     int m_year;
};
// Other methods are OK, mainly depending on the company's requirements. Generally, a prefix or suffix is added to distinguish.

2.3 call of constructor

Constructor has three calling methods, bracket method (commonly used), display method and implicit conversion method.

//1. Constructor classification
// According to the classification of parameters, it is divided into parametric and nonparametric constructions. Nonparametric construction is also called default constructor
// According to the type, it can be divided into ordinary construction and copy construction

class Person {
public:
	//Parameterless (default) constructor
	Person() {
		cout << "non-parameter constructor !" << endl;
	}
	//Parameterized constructor
	Person(int a) {
		age = a;
		cout << "Parameterized constructor!" << endl;
	}
	//copy constructor 
	Person(const Person& p) {
		age = p.age;
		cout << "copy constructor !" << endl;
	}
	//Destructor
	~Person() {
		cout << "Destructor!" << endl;
	}
public:
	int age;
};

//2. Call to constructor
//Call parameterless constructor
void test01() {
	Person p; //Call parameterless constructor
}

//Call a constructor with parameters
void test02() {

	//2.1 bracket method, commonly used
	Person p1(10);
	//Note 1: when calling the parameterless constructor, you can't add parentheses. If you add parentheses, the compiler thinks this is a function declaration
	//Person p2();	 ❌

	//2.2 explicit method
	Person p2 = Person(10); 
	Person p3 = Person(p2);
	//Writing Person(10) alone is an anonymous object. After the current line ends, it will be destructed immediately

	//2.3 implicit transformation method
	Person p4 = 10; // Person p4 = Person(10); 
	Person p5 = p4; // Person p5 = Person(p4); 

	//Note 2: anonymous objects cannot be initialized with the copy constructor. The compiler considers them object declarations
}

int main() {

	test01();
	//test02();
	return 0;
}
  • By default, the c + + compiler adds at least three functions to a class

    • Default constructor (no parameters, empty function body)
    • Default destructor (no parameters, empty function body)
    • The default copy constructor copies the values of attributes
  • Calling rules for constructor:

    • If the user defines a parameter constructor, c + + no longer provides the default no parameter constructor, but will provide the default copy constructor. You have to provide the default constructor yourself;

    • If you define copy constructors, c + + will not provide other constructors, that is, you have to provide default no and parametric constructors yourself;

      class Person {
      public:
      	//Parameterless (default) constructor
      	Person() {
      		cout << "non-parameter constructor !" << endl;
      	}
      	//Parameterized constructor
      	Person(int a) {
      		age = a;
      		cout << "Parameterized constructor!" << endl;
      	}
      	//copy constructor 
      	Person(const Person& p) {
      		age = p.age;
      		cout << "copy constructor !" << endl;
      	}
      	//Destructor
      	~Person() {
      		cout << "Destructor!" << endl;
      	}
      public:
      	int age;
      };
      
      void test01()
      {
      	Person p1(18);
      	//If you do not write a copy construct, the compiler will automatically add a copy construct and do a shallow copy operation
      	Person p2(p1);
      
      	cout << "p2 Your age is: " << p2.age << endl;
      }
      
      void test02()
      {
          //You can annotate the corresponding structure for verification.
      	//If the user provides a parameter construct, the compiler will not provide a default construct, but a copy construct
      	Person p1; //At this time, if the user does not provide a default construct, an error will occur
      	Person p2(10); //User provided parameters
      	Person p3(p2); //At this time, if the user does not provide a copy construct, the compiler will provide it
      
      	//If the user provides a copy construct, the compiler does not provide other constructors
      	Person p4; //At this time, if the user does not provide a default construct, an error will occur
      	Person p5(10); //At this time, if the user does not provide a parameter, an error will occur
      	Person p6(p5); //Users provide their own copy structure
      }
      
      int main() {
      
      	test01();
          test02();
      	return 0;
      }
      

3. Destructor

3.1 concept

Previously, through the study of constructors, we know how an object comes from and how that object doesn't come from?

The answer is the destructor: the function of the destructor is just the opposite of that of the constructor. It is used to complete some cleaning work before the object is deleted, that is, special finishing work. That is, 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:

  1. The destructor name is preceded by the character ~.

  2. No parameter, no return value.

  3. A class has only one destructor. If not explicitly defined, the system automatically generates a default destructor.

  4. At the end of the object life cycle, the C + + compilation system automatically calls 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()	
         {
             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;
    };
    
  5. Will the compiler automatically generate destructors do something? In the following program, we will see that the default destructor generated by the compiler will call its destructor for custom type members.

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

3.3 class objects as class members

When we understand construction and destruct, let's look at the call order of construction and destruct when class objects are class members.

For example:

class A {}
class B
{
    A a;
}
  • Class B has object A as A member and A as an object member. When creating A B object, who is the first and who is the second in the order of construction and Deconstruction of A and B?

    class Phone
    {
    public:
    	Phone(string name)
    	{
    		m_PhoneName = name;
    		cout << "Phone structure" << endl;
    	}
    
    	~Phone()
    	{
    		cout << "Phone Deconstruction" << endl;
    	}
    
    	string m_PhoneName;
    
    };
    
    class Person
    {
    public:
    
    	//The initialization list tells the compiler which constructor to call
    	Person(string name, string pName) :m_Name(name), m_Phone(pName)
    	{
    		cout << "Person structure" << endl;
    	}
    
    	~Person()
    	{
    		cout << "Person Deconstruction" << endl;
    	}
    
    	void playGame()
    	{
    		cout << m_Name << " use" << m_Phone.m_PhoneName << " Brand mobile phone! " << endl;
    	}
    
    	string m_Name;
    	Phone m_Phone;
    
    };
    void test01()
    {
    	//When a member in a class is an object of another class, we call it an object member
    	//The order of construction is: call the construction of object members first, and then call the construction of this class
    	//The tectonic sequence is opposite to that of the structure
    	Person p("Zhang San" , "Apple X");
    	p.playGame();
    
    }
    
    int main() {
    
    	test01();
    
    	return 0;
    }
    

    Summary: when a class object is a class member, the construction order is to call the construction of the object member first, and then call the construction of this class. The destruction order is opposite to the construction.

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 an 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

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

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

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

    class Date
    {
    public:
         Date(int year = 1900, int month = 1, int day = 1)
         {
             _year = year;
             _month = month;
             _day = day;
         }
         Date(const Date& d)
         {
             _year = d._year;
             _month = d._month;
             _day = d._day;
         }
    //    Date(const Date d) 	// If the parameter is not passed by reference and the value is passed, it is equivalent to copying a temporary copy. The formal parameter const date D of the current function will be copied again according to the passed value, and the copy structure will trigger the copy structure and recurse infinitely
    //     {
    //         _year = d._year;
    //         _month = d._month;
    //        _day = d._day;
    //     }
    private:
         int _year;
         int _month;
         int _day;
    };
    int main()
    {
         Date d1;
         Date d2(d1);
         return 0;
    }
    

    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 copy is called shallow copy, or value copy.

    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;
    };
    int main()
    {
         Date d1;
        // Here, the default copy structure called by d2 completes the copy, and the values of d2 and d1 are the same.
         Date d2(d1);
         return 0;
    }
    

    4. Then the default copy constructor generated by the compiler can copy the value of byte order. Do we still need to implement it ourselves? Of course, classes such as date classes are not necessary. What about the following classes? Try to verify it?

    // Here you will find that the following program will crash? This needs to be solved by the deep copy we will talk about later.
    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);
    }
    
    

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 + cannot be changed;
  • When an overloaded function is a class member, its formal parameters appear to be 1 less than the number of operands. The operator of the member function has a default formal parameter this, which is limited to the first formal parameter;
  • (member access operator). (member pointer access operator)); (domain operator)) sizeof (length operator))?: (conditional operator) these five operators cannot be overloaded*
// Global 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;
};
// It will be found here that if the operator is overloaded to be global, the member variables need to be shared. Then the problem comes. How to ensure the encapsulation?
// In fact, this can be solved with the friends we will learn later, or simply overloaded into member functions.
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;
}
class Date
{ 
public:
     Date(int year = 1900, int month = 1, int day = 1)
     {
         _year = year;
         _month = month;
         _day = day;
     }
 
 // bool operator==(Date* this, const Date& d2)
 // It should be noted here that the left operand is the object of the calling function pointed to by this
     bool operator==(const Date& d2)
     {
         return _year == d2._year
         && _month == d2._month
         && _day == d2._day;
     }
private:
     int _year;
     int _month;
     int _day;
};
void Test ()
{
     Date d1(2018, 9, 26);
     Date d2(2018, 9, 27);
     cout<<(d1 == d2)<<endl;
}

5.2 overload of assignment operator

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

Assignment operators mainly have four points: parameter type, return value, check whether you assign a value to yourself, and return * this (to realize the operation of concatenation, a=b=c)

  • If a class does not explicitly define the overload of assignment operator, the compiler will also generate one to complete the value copy of the object in byte order. For example:
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;
};
int main()
{
     Date d1;
     Date d2(2018,10, 1);

     // Here, the compiler called by d1 generates operator = to complete the copy, and the values of d2 and d1 are the same.
     d1 = d2;
     return 0;
}

  • Then, the default assignment overloaded function generated by the compiler can copy the value of byte order. Do we need to implement it ourselves? Of course, classes such as date class are not necessary. What about the following classes? Verify and try?

    // Here, you will find that the following programs will crash? In fact, it is still a problem of deep and shallow copy, which needs to be solved by deep copy in the future.
    class String
    {
    public:
     String(const char* str = "")
     {
         _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("world");
    
         s1 = s2;
    }
    

    Although only assignment operator overloading is exemplified here, some other basic operator overloading are also commonly used. Rewriting operators is relatively simple and you can try it yourself.

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 ()
     {
     cout<<"Display ()" <<endl;
     cout<<"year:" <<_year<< endl;
     cout<<"month:" <<_month<< endl;
     cout<<"day:" <<_day<< endl<<endl ;
     }
     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 ();
     
     const Date d2;
     d2.Display ();
    }
    //Think about this:
    //Can const objects call non const member functions? Can non const objects call const member functions?
    //Can other non const member functions be called in const member functions? Can other const member functions be called in non const member functions? 
    
    • Const objects can only call const member functions, not non const member functions; non const objects can call const member functions.

      Cause: const actually modifies the implicit this pointer of the member function, that is, const Date* this, so the compiler reports an error when passing in a non const member function (the type does not match and cannot be converted from const pointer to non const pointer); the pointer of the non const object is Date* this, the non const pointer can be converted to const pointer, and const member function can be called,

    • Other non const member functions cannot be called in const member functions, but other const member functions can be called in non const member functions.

7. Overload of address fetching and const address fetching operators

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

  • 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
     {
         return this ;
     }
    private :
         int _year ; // year
         int _month ; // month
         int _day ; // day
    };
    
    

d2;
d2.Display ();
}
//Think about this:
//Can const objects call non const member functions? Can non const objects call const member functions?
//Can other non const member functions be called in const member functions? Can other const member functions be called in non const member functions?

- **const Object can only be called const Non member functions cannot be called const Member function; wrong const Object can be called const Member function**. 

  reason: const The actual decorated member function is implicit this Pointer, i.e const Date* this,Therefore, non const The compiler reported an error (type mismatch) when a member function was (cannot be retrieved from const Pointer converted to non const Pointer); wrong const The pointer to the object is Date* this,wrong const Pointers can be converted to const Pointer, can call const Member functions,

- **const Other non member functions cannot be called within a member function const Member function, but not const Other functions can be called in member functions const Member function.**

## 7. Overload of address fetching and const address fetching operators

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

- 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!

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

Tags: C++

Posted on Mon, 20 Sep 2021 10:03:33 -0400 by rickphp