Explore the internal mechanism of C++ assignment operator overloading: a step-by-step guide to mastering it

W…Y’s homepage

Code repository sharing

Foreword:

In the previous blog, we have understood and learned the constructors and destructors in the initialization and cleanup modules, as well as the copy and copy functions in copy and copy. They are all important members of classes and objects. Today we are going to talk about them. Let’s copy another very important content in the copy module – assignment overloading. But while learning assignment overloading, we should first learn operator overloading. Without further ado, let’s get started!

Table of Contents

assignment operator overloading

Operator overloading

assignment operator overloading


Assignment operator overloading

Operator overloading

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

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// Date(const Date & amp; d) // Correct way to write
void print()
{
cout << _year << '/' << _month << '/' << _day << endl;
}
Date(const Date & amp; d) // Wrong writing: Compilation error will cause infinite recursion
{
_year = d._year;
_month = d._month;
_day = d._day;
}

private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(2022,10,23);
int x = 1;
    int y = 2;
    bool ret = x > y;
return 0;
}

Above picture is the disassembly code of the comparison, we can see that the compiler can directly make operator judgments.

Under normal circumstances, it is very easy for us to compare built-in types, because built-in types are some basic variables int, char… We can directly use operators ==, <, >, etc. to compare. But how should we deal with custom types?

In general, we can create a comparison function ourselves for comparison:

//Determine whether the previous date is greater than the following date
//Big returns true, small returns false
bool Greater(Date x, Date y)
{
if (x._year > y._year)
{
return true;
}
else if (x._year == y._year & amp; & amp; x._month > y._month)
{
return true;
}
else if (x._year == y._year & amp; & amp; x._month == y._month & amp; & amp; x._day > y._day)
{
return true;
}

return false;
}

Put this function into a class to access private data, or make the basic data public!

The above function can easily solve the size ratio problem, but it still has many shortcomings. For example, if the function name is not standardized, we will not understand the function of the function. Therefore, for the comparison of built-in types, C++ makes some provisions for naming:

The function name is: the keyword operator is followed by the operator symbol that needs to be overloaded.
Function prototype: return value type operator (parameter list)

Note:
New operators cannot be created by concatenating other symbols: such as operator@
Overloaded operators must have a class type parameter
Operators used for built-in types, their meaning cannot be changed, for example: the built-in integer type + , its meaning cannot be changed
When overloaded as a class member function, its formal parameters appear to be 1 less than the number of operands, because the first parameter of the member function is implicit
Hidden this

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

Then we can modify the above function like this:

bool operator>(const Date & amp; x, const Date & amp; y)
{
if (x._year > y._year)
{
return true;
}
else if (x._year == y._year & amp; & amp; x._month > y._month)
{
return true;
}
else if (x._year == y._year & amp; & amp; x._month == y._month & amp; & amp; x._day > y._day)
{
return true;
}

return false;
}

When selecting parameters, we choose to reference rather than pass parameters directly, which can save time and efficiency without calling the copy constructor.

In this way, our users can see at a glance the role of this function, which is a function used to compare sizes. However, the function name is still too long when calling the function in this way, and it is not straightforward and beautiful, so we can call it in a very simple way when using it:

bool ret1 = d1 > d2;

Only one operator is needed to call this function. Isn’t it very convenient? The compiler can help us turn this sentence into a statement that calls a function.

Note: Operator overloading and function overloading both have the word overload, but there is absolutely no relationship. Function overloading is a function with the same name that allows different parameters. Operator overloading is a custom type that can use operators directly. . Not everyone named “Zhang” is related by blood.

But when we compiled everything, we still got an error: Why is this?

Why is there one more parameter? Where is the excess? Member parameters will have a hidden parameter this pointer. So parameter mismatch causes program problems. It is equivalent to using two parameters when calling the function, but there are three parameters set!

How should we solve the problem? Just delete one.

#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace std;
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// Date(const Date & amp; d) // Correct way to write
void print()
{
cout << _year << '/' << _month << '/' << _day << endl;
}
//Date(const Date & amp; d) // Wrong writing: Compilation error will cause infinite recursion
//{
// _year = d._year;
// _month = d._month;
// _day = d._day;
//}
bool operator>(const Date & y)
{
if (_year > y._year)
{
return true;
}
else if (_year == y._year & amp; & amp; _month > y._month)
{
return true;
}
else if (_year == y._year & amp; & amp; _month == y._month & amp; & amp; _day > y._day)
{
return true;
}

return false;
}

private:
int _year;
int _month;
int _day;
};
//Determine whether the dates are equal
//bool Greater(Date x, Date y)
//bool Compare1(Date x, Date y)


int main()
{
Date d1;
Date d2(2022,10,23);
/*d1 == d2;
d1 > d2;*/
bool ret1 = d1 > d2;
int x = 1, y = 2;
bool ret = x > y;
return 0;
}

We just delete one parameter, and the compiler will convert it to d1.operator(d2) and then convert it to d1.operator(&d1,d2). The address is the content pointed to by this pointer.

Once we have learned about operator overloading, it is very easy to overload assignment operators.

Assignment operator overloading

Assignment operator overloading format
Parameter type: const T & amp;, passing reference can improve the efficiency of parameter passing
Return value type: T & amp;, returning a reference can improve the efficiency of return, and the purpose of returning a value is to support continuous assignment
Check whether you assign a value to yourself
Return *this: To compound the meaning of continuous assignment

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 != & amp;d)
   {
      _year = d._year;
      _month = d._month;
      _day = d._day;
   }
   
    return *this;
}
private:
    int _year;
    int _month;
    int _day;
};

The assignment operator can only be overloaded as a member function of the class and cannot be overloaded as a global function

class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
int _year;
int _month;
int _day;
};
// The assignment operator is overloaded into a global function. Note that when overloaded into a global function, there is no this pointer and two parameters need to be given.
Date & amp; operator=(Date & amp; left, const Date & amp; right)
{
if ( & amp;left != & amp;right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}
// Compilation failed:
// error C2801: "operator=" must be a non-static member

Reason: If the assignment operator is not implemented explicitly, the compiler will generate a default one. At this time, the user can implement it himself outside the class.
A global assignment operator overload conflicts with the default assignment operator overload generated by the compiler in the class, so assignment
Operator overloading can only be member functions of a class. User does not have When implemented explicitly, the compiler will generate a default assignment operator overload that copies byte by value. Note
Note: Built-in type member variables are directly assigned, while custom type member variables need to call the assignment operator of the corresponding class.
Overloading completes assignment

class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
}
Time & amp; operator=(const Time & amp; t)
{
if (this != & amp;t)
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
}
return *this;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
//Basic type (built-in type)
int _year = 1970;
int _month = 1;
int _day = 1;
// Custom type
Time_t;
};
int main()
{
Date d1;
Date d2;
d1 = d2;
return 0;
}

Now that the default assignment operator overloaded function generated by the compiler can complete byte order value copying, you still need to implement it yourself.
Now? Of course classes like date classes are not necessary. What about the following classes? Try verifying it?

// Will you find that the following program will crash? This requires deep copying, which we will talk about later, to solve it.
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array)
{
perror("malloc failed to apply for space");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(const DataType & data)
{
// CheckCapacity();
_array[_size] = data;
_size + + ;
}
~Stack()
{
if(_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
DataType *_array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack s2;
s2 = s1;
return 0;
}

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
Need to be implemented.

The above is all about assignment operator overloading! ! ! Thank you all for watching.