Overload assignment operator
Assignment operators may be the easiest to confuse, so heavy load it must be very careful.
1. The value operator can only be overloaded as a member function.
C does not allow assignment operators to be overloaded as global form, because if we can write out the global form of assignment operator functions, we can write such a function:
IINT Operator = (int A, integer b);
This occurs unable to say a day:
Integer A (3);
2 = a; // god save me
2. Pay attention to the situation of self-assignment
Now we write a simple Integer class and overload the assignment operator.
Class integer
{
INT I;
PUBLIC:
Integer (INT J): I (J) {};
Integer & Operator = (Const Integer & A)
{
i = a.i;
Return * this;
}
}
Yes, Not Bad. But, and slow, you don't consider self-assignment. Ah, is it necessary? Indeed, in this example, you can't find the reason for the self-assignment, but please see the following example:
Class CA
{
PUBLIC:
Char * p;
CA () {p = null;};
Void Set (Char * PSTR)
{
Delete [] P;
IF (pstr == null)
{
p = null;
}
Else
{
P = new char [strlen (pstr) 1];
STRCPY (P, PSTR);
}
}
CA & Operator = (CA & A)
{
Cout << "Operator = Invoked / N" << Endl;
// No self-assignment
Delete [] P;
p = a.p;
a.p = null;
Return * this;
}
~ CA () {delete [] p;};
}
The CA object "has" its member P pointing to the memory. Therefore, in the assignment function, the parameter A will give it to "ownership" and transfer it to the calling object. (The smart pointer defined in the C flag library is a "own" type smart pointer, which also exists in this "ownership transfer" nature)
Please see the example code below (Example Code 1):
CA A1, A2;
A1.SET ("OK");
A2 = a1;
Our function looks very good, but please see the following statement:
A2 = a2; // Tragedy happened, A2 "owned" memory is released!
Therefore, the assignment operator should be written as the following form:
CA & CA :: Operator = (CA & A)
{
Cout << "Operator = Invoked / N" << Endl;
// Detect self-assignment
IF (this! = & a)
{
Delete [] P;
p = a.p;
a.p = null;
}
Return * this;
}
Because it may cause harm to objects in the case of self-assignment, you must pay attention to self-assignment when you overload assignment operators. The so-called habits are natural, if we develop good habits, we will avoid brevity.
So the assignment operator function in the Integer class should be written:
Integer & Integer :: Operator = (Const Integer & A)
{
IF (this! = & a)
i = a.i;
Return * this;
}
3. Why is the assignment operator not called?
Now, our CA class has a "perfect" assignment operator function, let us sit down and write such a code (example code 2), and wait it to print Operator = Invoked:
CA A1;
A1.SET ("OK");
CA A2 = A1;
but……
God, our program crashes.
Debug certificate, this code does not call the assignment operator, why?
If you carefully check the example code 1 and 2, you will find that the difference between them is only: when the A2 is defined in code 2, it is initialized to the value of A1 ...
Wait, what do you think of, yes, it is it: copy constructor. C guarantees objects will be initialized, so CA A2 = A1; does not call the assignment operator but call the copy constructor. Because the copy constructor is not defined in the class, the compiler generates a default copy constructor. This function is just a simple BitCopy, and the memory of "owned" is not transferred to the A2, so that the memory is "owned by the two objects", when the object destructure, it was DELETE twice, so tragedy happened.
So, we need to define your own copy constructor:
Class CA
{
PUBLIC:
CA (CA & A)
{
Cout << "Copy Constructor" << Endl;
p = a.p;
a.p = null;
}
......
}
Because the parameters will change in the function, the parameters cannot be defined as const.
No matter what the code 1 is executed or code 2 is executed.
Before this section, I will ask you again: What happens if the return value type of the assignment operator is changed from Ca & CA to CA?
Ok, let's perform an example code 1 to see the results:
Operator = invoked
Copy Constructor // What is this sentence?
Well, yes, the assignment operator function is called. But why will I call a copy constructor?
Replace the A2 = A1 in code 1; replaced with a function, will help us find the answer:
A2 = a1; equivalent to a2.operator = (a1); and the function operator = will return a CA object, so the compiler generates a temporary variable and calls the copy constructor to initialize it. And then, this object is destroyed and the destructor is called. Now, let's add a printed statement to the CA's constructor and the destructor, we will clearly see this process.
Class CA
{
PUBLIC:
INT * P;
CA ()
{
COUT << "constructor" << endl;
p = null;
}
~ CA ()
{
Cout << "destructor" << Endl;
Delete [] P;
}
......
}
Exemplary code 1, the result is:
Constructor // a1 constructor
Constructor // A2 constructor
Operator = INVOKED / / Assignment statement
Copy Constructor // Temporary Variable Copy Construction Function
Destructor // Temteral Variables Destructor // A2 is destructed
Destructor // A1 is destructed
Temporary variable generation, call copy constructor, then destructure, I don't know if you know what you mean: When we call A2 = A1; when we call A2 = A1; when the "owned" memory is transferred to A2, then Transfer to that temporary variable, finally, when the temporary variable sector is destructed! This is certainly not what we want, or the return value type of the assigned operator function is defined as Ca &.
4. Automatically created assignment operator
Now think about it, if we don't define what the assignment operator in the CA will happen, do you have an example of A2 = A1 in Code 1 cause a compilation error? Of course, the compiler will automatically create one for us. This operator is imitated automatically created copy constructor: If the class contains objects (or inherited from other classes), the operator '=' is recursively called, which is called member assignment. For a detailed discussion of this issue, please see the first edition of "C Programming Thoughts", Chapter 11 (P225).