[Detailed C++] In-depth understanding of classes and objects (six default member functions)

Reminder

This article has a total of 6736 words, and it takes about 20 minutes to read it.

written in front

This article is the second in the explanation of C++ classes and objects. By the time of this article, we have really started to get to the really difficult place for classes and objects. This article focuses on the explanation of the 6 default member functions of the class, and I hope it will be helpful to you.

The 6 default member functions of the class

In the previous article, I mentioned empty classes, that is, classes with no members.

But in fact, is there really nothing in the empty class? No, when any class does not write anything, the compiler will automatically generate the following 6 default member functions.

Default member function: The member function generated by the compiler without explicit implementation by the user is called the default member function.

Constructor

concept introduction

Let's take a look at the following code:

class date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "." << _month << "." << _day << endl;
	}

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

int main()
{
	date d1;//define object
	d1.Init(2023, 1, 1);//initial assignment
	d1.Print();

	date d2;//define object
	d2.Init(2023, 1, 7);//initial assignment
	d2.Print();

	return 0;
}

Whenever we define a date class object, we need to use the Init member function to initialize the object before we can start using it. It's a little cumbersome to use. In fact, whether it is a custom type or a built-in type, when we define an object and use it, we always need to initialize the object, assign it to the value we need, and start using it.

For the custom type, we only need to use the assignment symbol assignment directly when defining to complete the initialization. Even without manual initialization, this object will have a random initial value.

For custom types, we also have constructors to simplify the initialization steps, and it is not always necessary to manually call the Init function like the above code.

The constructor is a special member function with the same name as the class name, which is automatically called by the compiler when creating a class type object to ensure that each member object has an appropriate initial value, and only ( called once by the compiler).

characteristic

The constructor is a special member function. It should be noted that although the name of the constructor has a structure, the main task of the constructor is not to open space to create objects, but to initialize objects. The following are the properties of the constructor:

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

  2. No return value.

  3. The compiler automatically calls the corresponding constructor when the object is instantiated.

  4. Constructors can be overloaded.

    class date
    {
    public:
    	//no-argument constructor
    	date()
    	{}
    
    	//Constructor with parameters
    	date(int year,int month,int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	void Print()
    	{
    		cout << _year << "." << _month << "." << _day << endl;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	date d1(2022,12,31);
    	//call a parameterized constructor
    	d1.Print();
    
    	date d2;
    	//call no-argument constructor
    	d2.Print();
    
    	return 0;
    }
    

    Run the screenshot:

    Precautions:

    int main()
    {
    	//Note: When using a parameterless constructor to define a variable, the object does not need to be followed by parentheses, otherwise it becomes a function declaration
    	//Like the following code, it becomes a function declaration named d, the return value is date class, no parameters
    	date d();
    	//warning C4930: 'date d(void)': prototype function not called (was it intentionally defined with a variable?)
    
    	return 0;
    }
    
  5. If no constructor is explicitly defined in the class, the C++ compiler will automatically generate a default constructor without parameters. Once the user explicitly defines it, the compiler will no longer generate it.

    • explicit definition

      class date
      {
      public:
      	//A constructor with parameters, the constructor has been defined at this time, the compiler will no longer generate a default constructor without parameters
      	date(int year, int month, int day)
      	{
      		_year = year;
      		_month = month;
      		_day = day;
      	}
      
      	void Print()
      	{
      		cout << _year << "/" << _month << "/" << _day << endl;
      	}
      
      private:
      	int _year;
      	int _month;
      	int _day;
      };
      
      int main()
      {
      	//There is no constructor without parameters, and parameters must be passed in for initialization. At this time, the compilation fails
      	//error C2512: 'date' : no suitable default constructor available
      	date d1;
      
      	return 0;
      
      }
      
    • Not explicitly written, the compiler automatically generates

      class date
      {
      public:
      	/*
      	//A constructor with parameters, the constructor has been defined at this time, the compiler will no longer generate a default constructor without parameters
      	date(int year, int month, int day)
      	{
      		_year = year;
      		_month = month;
      		_day = day;
      	}*/
      
      	void Print()
      	{
      		cout << _year << "/" << _month << "/" << _day << endl;
      	}
      
      private:
      	int _year;
      	int _month;
      	int _day;
      };
      
      int main()
      {
      	//There is a default constructor without parameters automatically generated by the compiler, and the compilation passes
      	date d1;
          d1.Print();
          
      	return 0;
      }
      

      Run the screenshot:

  6. Through the demonstration of the above code, we will find that if the constructor is not implemented, the compiler will generate a default constructor, but it seems that the default constructor is useless. The generated default constructor does not process member objects, and the data is still a random value. That is to say, the default constructor generated by the compiler is of no use here? ? ?

    Actually not. C++ divides types into built-in types (basic types) and custom types. The built-in type is the data type provided by the language, such as: char, int, double..., and the custom type is the type we define ourselves using class/struct/union. Through the following code, we will find that the default constructor generated by the compiler will call its corresponding default member function on the custom type member object.

    class Time
    {
    public:
    	//no-argument default constructor
    	Time()
    	{
    		cout << "Time()" << endl;
    		_hour = 0;
    		_min = 0;
    		_sec = 0;
    	}
    private:
    	int _hour;
    	int _min;
    	int _sec;
    };
    
    class date
    {
    private:
    	//built-in type
    	int _year;
    	int _month;
    	int _day;
    
    	//custom type
    	Time _t;
    };
    
    int main()
    {
    	date d1;
    	//No parameters are passed in, and the internal compiler of the class is called to generate a default constructor with no parameters
    	//For custom type member objects, the generated default constructor will call the no-argument default constructor of the class
    
    	return 0;
    }
    

    Run the screenshot:

    That is to say, the default constructor generated by the compiler does not initialize the built-in type member object, and calls its default constructor for the user-defined type member object.

    Note: In C++11, a patch has been applied for the defect that the built-in type members are not initialized, that is, the built-in type member variables can be given a default value when declared in the class.

  7. Both the parameterless constructor and the default constructor are called default constructors, and there can only be one default constructor.

    Note: No-argument constructors, full default constructors, and constructors generated by the compiler without implementing them can all be considered default constructors.

    class date
    {
    public:
    	date()
    	{
    		_year = _month = _day = 0;
    	}
    
    	date(int year = 1970, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	void Print()
    	{
    		cout << _year << "/" << _month << "/" << _day << endl;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	date d1;
    	//Compilation fails
    	//error C2668: 'date::date' : ambiguous call to overloaded function
    
    	return 0;
    }
    
  8. In C++, default parameters of function parameters can be assigned using malloc, new, or other functions. At this point, constructors are just like any other normal function. The default values ​​set for member objects of built-in types can also be used.

    But is this a good way? Functions like malloc need to check the return value. In this way, how to check the return value? It is recommended to use this method as little as possible. The class actually has a better initialization scheme, which I will explain later.

destructor

concept introduction

A class object has a constructor, so that it can have a relatively standard initialization behavior like a basic type (built-in type), but how can this object have a relatively standard destruction behavior like a basic type? Therefore, there is a destructor.

Destructor: Contrary to the function of the constructor, the destructor does not complete the destruction of the object itself, and the local object destruction is done by the compiler. When the object is destroyed, it will automatically call the destructor to complete the cleanup of resources in the object.

characteristic

A destructor is a special member function whose characteristics are as follows:

  1. The destructor name is prefixed with the character ~ before the class name.

  2. No parameters and no return type.

  3. A class can have only one destructor. If not explicitly defined, the system will automatically generate a default destructor. Note: Destructors cannot be overloaded

  4. When the object life cycle ends, the C++ compilation system automatically calls the destructor.

    class date
    {
    public:
    	//destructor
    	~date()
    	{
    		cout << "~date()" << endl;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    void test()
    {
    	date d1;
    	//When the object life cycle ends, the compiler automatically calls the destructor
    }
    
    int main()
    {
    	test();
    
    	return 0;
    }
    

    Run the screenshot:

  5. Does something get done with regards to the destructors automatically generated by the compiler? In the following program, we will see that the default destructor generated by the compiler calls its destructor for the custom type member.

    class Time
    {
    public:
    	~Time()
    	{
    		cout << "~Time()" << endl;
    	}
    private:
    	int _h;
    	int _m;
    	int _s;
    };
    
    class date
    {
    private:
    	//built-in type
    	int _year;
    	int _month;
    	int _day;
    
    	//custom type
    	Time _t;
    };
    
    int main()
    {
    	date d1;
    
    	return 0;
    }
    

    Run the screenshot:

    It can be seen that ~Time () is output after the program runs, but the Time class object is not directly created in main, why is the destructor of the Time class called at the end?

    Because the date object d is created in the main function, and d contains 4 member variables, including three basic types (built-in types). For built-in type members, resource cleaning is not required when destroying them. Finally, the system can directly reclaim their memory .

    The _t member object is an object of the Time class, so when the object d is destroyed, the _t object of the Time class contained in it must be destroyed, so the destructor of the Time class must be called.

    It should be noted that the destructor of the Time class is not directly called here, but the destructor of the date is called, and the date class does not explicitly provide a destructor. So the compiler will generate a default destructor for the date class, and call the destructor of the Time class in it.

    That is, when the Date class object is destroyed, it is necessary to ensure that each custom type member object inside it can be destroyed correctly. The destructor generated by default does not process built-in types, and its destructor will be called for custom types.

    Note: The constructor of the class is called when the object of the class is created, and the destructor of the class is called when the class is destroyed.

  6. If there is no resource application in the class, the destructor can not be written, and the default destructor generated by the compiler, such as the date class, must be written when there is a resource application, otherwise it will cause resource leakage, such as the stack class, etc. .

copy constructor

concept introduction

In the computer, the shortcut key combinations of Ctrl+C (copy) and Ctrl+V (paste) are almost the most commonly used by us. After all, the copy and paste function saves operation steps and improves efficiency. In C/C++, for built-in types, we often use an existing object to initialize another object to get a copy of the object.

In C++, when we create a custom type object, we certainly want to be able to easily use an existing object of this type to initialize the object like a built-in type. So, there is a copy constructor.

Copy constructor: There is only a single formal parameter, which is a reference to the object of this class type (usually const decoration is commonly used), and then automatically called by the compiler when creating a new object with an existing object of this class type.

feature

Special member functions with the following characteristics:

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

  2. The parameter of the copy constructor is only one and must be a reference to a class type object, and the compiler will directly report an error if the value-passing method is used, because it will cause infinite recursive calls.

    class date
    {
    public:
    	date(int year = 1970, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    
    	date(const date d)//typo
    	{
    		_year = d._year;
    		_month = d._month;
    		_day = d._day;
    	}
    
    
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    int main()
    {
    	date d1;
    	date d2(d1);
        //Error under vs
    	//error C2652: 'date': illegal copy constructor: first argument should not be 'date'
        //Error under g++
        //error: invalid constructor; you probably meant 'date (const date&)'
    	return 0;
    }
    

    Schematic diagram of dead recursion:

    Correct spelling:

    Note: You can see that I used const to modify this reference parameter here, because we don't want the object used for copying to be changed during the copying process, so I use const modification here.

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

    class Time
    {
    public:
    	Time()
    	{
    		_h = _m = _s = 0;
    	}
    
    	Time(const Time& t)
    	{
    		_h = t._h;
    		_m = t._m;
    		_s = t._s;
    		cout << "Time::Time(const Time& t)" << endl;
    	}
    	
    	void Print()
    	{
    		cout << _h << ":" << _m << ":" << _s << endl;
    	}
    
    //private:
    	int _h;
    	int _m;
    	int _s;
    };
    
    class date
    {
    public:
    	date(int year = 1970, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    		_t._h = _t._m = _t._s = 1;
    	}
    
    	void Print()
    	{
    		cout << _year << "/" << _month << "/" << _day << endl;
    		_t.Print();
    	}
    		
    private:
    	//built-in type
    	int _year;
    	int _month;
    	int _day;
    
    	//custom type
    	Time _t;
    };
    
    int main()
    {
    	date d1(2023, 1, 1);
    	date d2(d1);
    	d2.Print();
    
    	return 0;
    }
    

    Run the screenshot:

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

  4. The default copy constructor generated by the compiler can already complete byte order copying, do I need to explicitly implement it myself? Of course classes like the date class are not necessary. However, let's take a look at the simple Stack class below. Should this class implement the copy construction by itself?

    typedef int Datatype;
    class Stack
    {
    public:
    	//Constructor, apply for space to store data in the stack
    	Stack()
    	{
    		_array = new Datatype[16];
    		_size = 0;
    		_capacity = 16;
    	}
    
    	//Destructor, releases resources occupied by class objects
    	~Stack()
    	{
    		delete[] _array;
    		_size = _capacity = 0;
    	}
    
    	//Push data onto the stack
    	void push(const Datatype& x)
    	{
    		_array[_size++] = x;
    	}
    
    private:
    	Datatype* _array;
    	size_t _size;
    	size_t _capacity;
    };
    
    
    int main()
    {
    	Stack st1;
    	st1.push(1);
    	st1.push(2);
    	st1.push(3);
    	st1.push(4);
    
    	Stack st2(st1);
    
    	return 0;
    }
    

    Run the screenshot:

    Cause Analysis:

    Note: If there is no resource application involved in the class, the copy constructor can be written or not; once the resource application is involved, the copy constructor must be written, otherwise it is a shallow copy. (You can also refer to whether the destructor needs to be implemented by itself, if yes, then the copy constructor must also)

  5. Typical calling scenarios of copy constructor:

    • Create a new object using an existing object
    • The function parameter type is a class type object
    • The return value type of the function is a class type object
    class date
    {
    public:
    	date(int year = 1970, int month = 1, int day = 1)
    	{
    		cout << "date(int year, int month, int day)" << this << endl;
    	}
    
    	date(const date& d)
    	{
    		cout << "date(const date& d)" << this << endl;
    	}
    
    	~date()
    	{
    		cout << "~date()" << this << endl;
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    date test(date d)
    {
    	return d;
    }
    
    
    int main()
    {
    	date d1;
    	test(d1);
    
    	return 0;
    }
    

    Run the screenshot:

    Principle analysis:

    In order to improve program efficiency, when passing parameters to general objects, try to use reference (pointer) types, and when returning, use references (pointers) as much as possible to use references (pointers) according to the actual scene.

assignment operator overloading

operator overloading

Before talking about overloading assignment operators, let's briefly understand overloading operators. (Of course, if you are not clear about the concept of function overloading or want to understand its principle, you can take a look at my This article)

C++ introduces operator overloading to enhance the readability of the code. Operator overloading is a function with a special function name, and also has its return value type, function name, and parameter list. The return value type and parameter list are similar to ordinary functions.

Function name: the keyword (reserved word) operator followed by the operator symbol that needs to be overloaded

Function prototype: return value type operator operator (parameter list)

Notice:

  • New operators cannot be created by concatenating other symbols: e.g. operator@
  • An overloaded operator must have a class type (not a built-in type) parameter
  • It is not possible to redefine the meaning of operators used for objects of built-in types, such as the built-in integer +, cannot change its meaning, and cannot add the + operator function to built-in integer arrays.
  • The parameter list must have the same number of parameters as the operator (including the implicit this parameter if the operator is overloaded as a class member function). Left-to-right formal parameter positions correspond to left-to-right operand bits.
  • .* :: sizeof ?: . Note that the above 5 operators cannot be overloaded. (This often appears in written multiple choice questions)

Demo:

class date
{
public:
	date(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

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

//one
//defined as a non-member function
//But at this time, the member variables need to become public. This design affects the encapsulation, which is not good
bool operator==(const date& d1, const date& d2)
{
	return d1._year == d2._year 
		&& d1._month == d2._month 
		&& d1._day == d2._day;
}

int main()
{
	date d1(20231, 1, 1);
	date d2(20231, 1, 1);
	date d3;

	cout << (d1 == d2) << endl;
	//d1 == d2 The compiler will parse it into a function call like operator==(d1,d2)
	cout << (d1 == d3) << endl;
	//Similarly d1 == d3 -> operator==(d1,d3)

	return 0;
}

Run the screenshot:

class date
{
public:
	date(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//two 
	//Overloaded as a member function of a class
	//Note that the function parameter list at this time should be (date* const this, const date& d)
	//The left operand is this pointing to the object calling the function, which is also the object on the left side of the operator
	bool operator==(const date& d)
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}

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

int main()
{
	date d1(20231, 1, 1);
	date d2(20231, 1, 1);
	date d3;

	cout << (d1 == d2) << endl;
	//d1 == d2 The compiler will parse it into d1.operator==(d2) becomes a member function call of object d1
	cout << (d1 == d3) << endl;
	//Similarly d1 == d3 -> d1.operator==(d3)

	return 0;
}

Pre-++ and post-++ overloading

C++ has a special treatment for special (pre and post) unary operators such as ++ and --.

class date
{
public:
	date(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 
	// Pre-++ (--) and post-value ++ (--) are both unary operators, in order to allow pre-++ (--) and post-value ++ (--) to form correct overloading
	// C++ regulations: add an additional parameter of type int when the post ++ is overloaded, but this parameter does not need to be passed when calling the function, and the compiler will pass it automatically
	// 
	// simple demo
	// 
	//Front
	//Return the object after "+1" Here *this is the object, which will not be destroyed after the function ends, so you can use reference return to improve program efficiency
	date& operator++()
	{
		_day += 1;
		return *this;
	}
	//rear
	//Return the object before "+1" Here tmp is a local variable in the function and can only be returned by value
	date operator++(int)//Here you only need to write the type to add int to the parameter table
	{
		date tmp(*this);
		_day += 1;
		return *this;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

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

int main()
{
	date d1(20231, 1, 1);
	date d2 = ++d1;
	date d3 = d1++;

	d1.Print();
	d2.Print();
	d3.Print();

	return 0;
}

Run the screenshot:

assignment operator overloading

  1. assignment operator overload format

    • Parameter type: const T&, passing by reference can improve the efficiency of parameter passing.
    • Return value type: T&, return reference can improve the efficiency of return, and the purpose of return value is to support continuous assignment.
    • Check if you assign a value to yourself.
    • Return *this: for continuous assignment.
    class date
    {
    public:
    	date(int year = 1970, 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)//The parameter is a reference type
    	{
    		if (this != &d)//avoid self-assignment
    		{
    			_year = d._year;
    			_month = d._month;
    			_day = d._day;
    		}
    		return *this;//Returns the object itself by reference
    	}
    
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    
  2. Assignment operators can only be overloaded as member functions of a class and cannot be overloaded as global functions.

    class date
    {
    public:
    	date(int year = 1970, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    //private:
    	int _year;
    	int _month;
    	int _day;
    };
    
    //At this time, it is a non-member function without this pointer, so two parameters must be passed
    date& operator=(const date& d1,const date& d2)
    {
    	if (&d1 != &d2)//avoid self-assignment
    	{
    		d1._year = d2._year;
    		d1._month = d2._month;
    		d1._day = d2._day;
    	}
    	return d1;//Returns the object itself by reference
    }
    //Compilation failed
    //error C2801: 'operator=" must be a non-static member
    

    Reason: If the assignment operator is not explicitly implemented, the compiler will generate a default one. At this time, if the user implements a global assignment operator overload outside the class, it will conflict with the default assignment operator overload generated by the compiler in the class, so the assignment operator overload can only be a member function of the class.

  3. **When the user does not explicitly implement, the compiler will generate a default assignment operator overload, which is copied byte by byte in the form of value. **Note: Built-in type member variables are directly assigned, while custom type member variables need to call the assignment operator overload of the corresponding class to complete the assignment.

    class Time
    {
    public:
    	Time& operator=(const Time& t)
    	{
    		if (this != &t)
    		{
    			_h = t._h;
    			_m = t._m;
    			_s = t._s;
    			cout << "Time::operator=(const Time& t)" << endl;
    		}
    		return *this;
    	}
    
    private:
    	int _h;
    	int _m;
    	int _s;
    };
    
    class date
    {		
    private:
    	//built-in type
    	int _year;
    	int _month;
    	int _day;
    
    	//custom type
    	Time _t;
    };
    
    int main()
    {
    	date d1;
    	date d2;
    	d2 = d1;
    
    	return 0;
    }
    

    Run the screenshot:

    Similar to the copy constructor, the default assignment operator overloads generated by the compiler can perform byte-ordered value copying. For a class that needs to implement assignment operator overloading by itself, it is the same as needing to implement the copy constructor by itself. Take a look at this code:

    // The simple stack used above briefly demonstrates
    typedef int Datatype;
    class Stack
    {
    public:
    	//Constructor, apply for space to store data in the stack
    	Stack()
    	{
    		_array = new Datatype[16];
    		_size = 0;
    		_capacity = 16;
    	}
    
    	//Destructor, releases resources occupied by class objects
    	~Stack()
    	{
    		delete[] _array;
    		_size = _capacity = 0;
    	}
    
    	//Push data onto the stack
    	void push(const Datatype& x)
    	{
    		_array[_size++] = x;
    	}
    
    private:
    	Datatype* _array;
    	size_t _size;
    	size_t _capacity;
    };
    
    
    int main()
    {
    	Stack st1;
    	st1.push(1);
    	st1.push(2);
    	st1.push(3);
    	st1.push(4);
    
    	Stack st2;
    	st2 = st1;
    
    	return 0;
    }
    

    Run the screenshot:

    Principle analysis:

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

const member

The "member function" modified by const is called a const member function (note that this does not mean that const modifies the return value type), and the const modified class member function actually modifies the implicit this pointer of the member function, indicating that it cannot be used in this member function Modifications are made to any member of the class.

Take a look at the following code:

class date
{
public:
	date(int year = 1970, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << "Print()" << endl;
		cout << _year << "/" << _month << "/" << _day << endl;
	}

	void Print() const
	{
		cout << "Print() const" << endl;
		cout << _year << "/" << _month << "/" << _day << endl;
	}

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

int main()
{
	date d1(2023, 1, 1);
	const date d2(2023, 1, 1);

	d1.Print();
	d2.Print();

	return 0;
}

Consider the following questions:

  1. Can a const object call a non-const member function?

    Comment out the const-modified Print function in the above code, and you will find that the compilation fails.

    //error C2662: 'void date::Print(void)' : cannot convert 'this' pointer from 'const date' to 'date &'
    

    That is to say, const objects cannot call non-const member functions. The const-modified object cannot enter the member function of the member object that has permission to modify (the essence is that the point of the implicit pointer this is not const-modified, and the const object cannot be passed to the non-const-modified pointer when passing parameters). The enlargement of the permission is Not allowed.

  2. Can a non-const object call a const member function?

    Comment out the non-const-modified Print function in the above code, and the compilation runs normally.

    Run the screenshot:

    Contrary to the first case, non-const objects can call const member functions, non-const objects can pass parameters to const-modified pointers, and the reduction of permissions is allowed.

  3. Can const member functions call other non-const member functions?

    Screenshot of the experimental code:

    //error C2662: 'void date::test(void)' : cannot convert 'this' pointer from 'const date' to 'date &'
    

    Similar to the first question, const cannot be passed to non-const, and permissions cannot be enlarged.

  4. Can non-const member functions call other const member functions?

    Compile and run through, run the screenshot:

    Similar to the second question, non-const can be passed to const, and permissions can be narrowed.

Address and const address operator overloading

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

These two operators generally do not need to be overloaded, just use the default address-taking overload generated by the compiler. Only in special cases, overloading is required, such as wanting others to obtain the specified content!

epilogue

The above is the second explanation about classes. Congratulations for being able to read this. I hope my article can help you gnaw this difficult bone. If you think it’s not bad, please like, bookmark and share. Of course, if you find that I have written something wrong or have suggestions for me, please let me know in the comment area or private message.

egg

Blog source code: GitHUb gitee
Big is not allowed.

  1. Can a non-const object call a const member function?

    Comment out the non-const-modified Print function in the above code, and the compilation runs normally.

    Run the screenshot:

    [External link image transfer...(img-yU0OpVpw-1674282957080)]

    Contrary to the first case, non-const objects can call const member functions, non-const objects can pass parameters to const-modified pointers, and the reduction of permissions is allowed.

  2. Can const member functions call other non-const member functions?

    Screenshot of the experimental code:

    [External link image transfer...(img-mAdZhh50-1674282957080)]

    //error C2662: 'void date::test(void)' : cannot convert 'this' pointer from 'const date' to 'date &'
    

    Similar to the first question, const cannot be passed to non-const, and permissions cannot be enlarged.

  3. Can non-const member functions call other const member functions?

    Compile and run through, run the screenshot:

    [External link image transfer...(img-Wrh4Y5vW-1674282957080)]

    Similar to the second question, non-const can be passed to const, and permissions can be narrowed.

Address and const address operator overloading

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

These two operators generally do not need to be overloaded, just use the default address-taking overload generated by the compiler. Only in special cases, overloading is required, such as wanting others to obtain the specified content!

epilogue

The above is the second explanation about classes. Congratulations for being able to read this. I hope my article can help you gnaw this difficult bone. If you think it’s not bad, please like, bookmark and share. Of course, if you find that I have written something wrong or have suggestions for me, please let me know in the comment area or private message.

egg

Blog source code: GitHUb gitee

Tags: C++ programming language

Posted by Xster on Sun, 22 Jan 2023 06:58:01 +0300