01 assignment operator overload requirements
Sometimes you want the types on both sides of the assignment operator to be mismatched. For example, assign an int type variable to a Complex object, or assign a char * type string to a string object. In this case, you need to overload the assignment operator '='.
Note that the assignment operator = can only be overloaded as a member function.
02 example of overloaded assignment operator
Let's explain the overloaded function of assignment operator with an example of customizing a string class code of our own.
class MyString // String class { public: // Constructor, initializing 1-byte character by default MyString ():m_str(new char[1]) { m_str[0] = 0; } // Destructors, freeing resources ~MyString() { delete [] m_str; } const char* c_str() { return m_str; } // Assignment operator overload function // Overload = makes obj = "Hello" tenable MyString & operator= (const char *s) { // Release old string resources delete [] m_str; // Space size of new string generated, more length + 1 to store \ 0 m_str = new char[strlen(s) +1 ]; // Copy the contents of the new string strcpy(m_str, s); // Return the object return *this; } private: char * m_str; // String pointer }; int main() { MyString s; s = "Hello~"; // Equivalent to s.operator=("Hello ~"); std::cout << s.c_str() << std::endl; // MyString s2 = "Hello!"; / / if this statement is uncommented, it will compile and report an error s = "Hi~"; // Equivalent to s.operator=("Hi ~"); std::cout << s.c_str() << std::endl; return 0; }
Output results:
Hello~ Hi~
When the = operator function is overloaded, the s = "Hello ~"; statement is equivalent to the s.operator=("Hello ~");.
It should be noted that the above MyString s2 = "Hello!"; statement is actually an initialization statement, not an assignment statement. Because it is an initialization statement, the constructor needs to be called for initialization. Then a constructor with the char * parameter is required. Since we have not defined this constructor, there will be compilation errors.
03 shallow copy and deep copy
Based on the above example, suppose we want to implement the last statement:
MyString s1,s2; s1 = "this"; // Call overloaded assignment statement s2 = "that"; // Call overloaded assignment statement s1 = s2; // How to achieve this??
s1 = s2; the purpose of the statement is that the string placed by s1 object is the same as the string placed by s2 object. Because the similarity on both sides of the = sign is the object, the compiler will use the native assignment operator function, but this native assignment operator function is very dangerous for the object with pointer member variable!
Shallow copy
If the original assignment operator function is used to assign objects with pointer member variables, the pointer addresses of the two objects will be the same, that is to say, the pointer member variables of the two objects point to the same address, which is a shallow copy.
When an object releases a pointer member variable, the address pointed to by another object's pointer member variable is empty. When the object is used again, the program will crash, because the pointer member function of the object is already an illegal pointer!
Deep copy
If there are pointer member variables in the object, we need to use the native assignment operator function to prevent the occurrence of program error.
Therefore, add the following member functions to the class MyString class:
MyString & operator=(const MyString & s) { // Release old string resources delete [] m_str; // Space size of new string generated, more length + 1 to store \ 0 m_str = new char[strlen(s.m_str) +1 ]; // Copy the contents of the new string strcpy(m_str, s.m_str); // Return the object return *this; }
Is that enough? Is there anything else to improve?
We are considering the following statement:
MyString s; s = "Hello"; s = s; // Is there a problem?
Is there a problem with the last statement?
s = s; equivalent to s.operator=(s). Since s and s are the same object, it's unnecessary to perform the overloaded function of assignment = completely. Let's add another judgment. When the left and right sides are the same object, it's better to return the object directly:
MyString & operator=(const MyString & s) { // When the left and right sides are the same object, directly return the object if(this == &s) return *this; delete [] m_str; m_str = new char[strlen(s.m_str) +1 ]; strcpy(m_str, s.m_str); return *this; }
Discussion on operator = return value type
- How about void?
- How about MyString?
- Why mystring &?
When we overload an operator, the good style is to keep the original characteristics of the operator as much as possible
Consider:
- a = b = c; the order of this assignment statement is first b = c, then a = (b = c). If the returned void type, then a = (void) is obviously not valid;
- (a = b) = c; this assignment statement will modify the value of A. if the returned type is a MyString object, then the value of a cannot be modified.
They are equivalent to:
- a.operator=(b.operator=(c));
- (a.operator=(b)).operator=(c);
So to sum up, operator = return value type is mystring & which is better.
04 copy constructor
Is the above MyString class OK?
MyString s; s = "Hello"; MyString s1(s); // To consider this situation, overload the copy constructor
If the default copy (copy) constructor is used, there will be problems for objects with pointer member variables, because the default copy (copy) constructor will cause pointer member variables of two objects to point to the same space.
Therefore, it is necessary to overload the copy constructor and realize the way of deep copy:
MyString (const MyString &s) { m_str = new char[strlen(s.m_str) + 1]; strcpy(m_str, s.m_str); }
05 summary
All the final codes are as follows:
class MyString // String class { public: // Constructor, initializing 1-byte character by default MyString ():m_str(new char[1]) { m_str[0] = 0; } // Copy constructor MyString (const MyString &s) { m_str = new char[strlen(s.m_str) + 1]; strcpy(m_str, s.m_str); } // Destructors, freeing resources ~MyString() { delete [] m_str; } const char* c_str() { return m_str; } // Assignment operator overload function // Overload = makes obj = "Hello" tenable MyString & operator= (const char *s) { // Release old string resources delete [] m_str; // Space size of new string generated, more length + 1 to store \ 0 m_str = new char[strlen(s) +1 ]; // Copy the contents of the new string strcpy(m_str, s); // Return the object return *this; } // Assignment operator overload function // Overloading the = number enables obj 1 = obj 2 to hold MyString & operator=(const MyString & s) { // When the left and right sides are the same object, directly return the object if(this == &s) return *this; delete [] m_str; m_str = new char[strlen(s.m_str) +1 ]; strcpy(m_str, s.m_str); return *this; } private: char * m_str; // String pointer }; int main() { MyString s1,s2; s1 = "Hello~"; // Equivalent to s1.operator=("Hello ~"); std::cout << s1.c_str() << std::endl; s2 = "Hi~"; // Equivalent to s2.operator=("Hi ~"); std::cout << s2.c_str() << std::endl; s1 = s2; // Equivalent to s1.operator=(s2); std::cout << s1.c_str() << std::endl; MyString s3(s1); // copy constructor std::cout << s3.c_str() << std::endl; return 0; }
The output is as follows:
Hello~ Hi~ Hi~ Hi~