clause 10: Make operator= return a reference to *thisassignment operator operations are performed from right to left. For example, a chain assignment
<span style= "FONT-SIZE:14PX;" >int x, Y, z;x=y=z=15;</span>
The compiler interprets this as always:x= (y= (z=15));Assign a value to Z, assign a value to Y, and then assign the value to x with Y. to implement a chain assignment, the operator must return an argument that reference to the left of the operator. In fact, if Operator= does not return a reference and returns a temporary object, the chain assignment can still be implemented. However, the construction of this temporary object invokes the copy constructor. Look at the following example:
#include <iostream>using namespace Std;class widget{public:widget () {cout<< "Default Ctor" <<ENDL;} Widget (const widget& RHS) {cout<< "Copy Ctor" <<ENDL;} widget& operator= (const widget& RHS) {cout<< "operator=" <<endl;return *this;}; int main () {Widget A,b,c;a=b=c;return 0;}
This output is:Default Ctor
Default Ctor
Default Ctor
Operator=
Operator=If you remove the reference returned by operator=, change to Widget operator= (const widget& RHS)the output will be:Default Ctor
Default Ctor
Default Ctor
Operator=
Copy Ctor
Operator=
Copy Ctorreturns a temporary object that assigns a value to the left variable. One more step, a waste of resources.
operator= is to change the left operand, and its similar operator+=, operator-= and so on to change the operation of the left operator, should return the reference. This is an agreement that should be adhered to.
Article 11: Implementing "self-assignment" in operator=self-assignment refers to the assignment of an object to itself. Widget W;w=w;this looks a little silly, but it's legal. The above example makes it easy to find self-assigning values. But sometimes it's not that easy, for example: array aA[i]=a[j];when I and J are equal, self-assignment is the value. For example, two pointers px and py*px=*py;if two pointers point to the same object, this is also self-assignment. In addition, there are references. The more obscure self-assignment occurs in the base class and in the derived class hierarchy, and the assignment between pointers or references of different types can have self-assignment values.
If you follow terms 13 and 14, use objects to manage resources, and make sure that the resource management object has the right moves when copy occurs so that self-assignment is secure. If you manage resources yourself, you might "accidentally release it before you stop using the resource." For example, use a class to manage a pointer.
Class widget{public:widget& operator= (const widget& RHS) {delete p;p=new int (THS.P); return *this;} int *p;};
If the above code is self-assigned, it is freed before using the pointer p.The way to prevent this from happening is to "test it" and judge whether it is self-assignment before deleting it.
Class widget{public:widget& operator= (const widget& RHS) {if (THIS==&RHS)//certificate with Test return *this;delete p;p=new int (RHS.P); return *this;} int *p;};
This version of operator= can solve the problem of self-assignment. But there is another problem: exceptional security. If delete p succeeds, but p=new Int (RHS.) What happens when you fail?at this point, the Widget object holds a pointer to the memory being freed. The following methods can be used for exception security.
Class widget{public:widget& operator= (const widget& RHS) {int tmp=p;//record original memory p=new int (RHS.P);d elete tmp;// Release the original memory return *this;} int *p;};
In the implementation of exception security, but also to obtain the security of self-assigned value. if P=new int (THS.P) has an exception, subsequent delete tmp will not execute.If you are concerned about efficiency, you can put the "certificate with test" at the beginning of the function. But how often does "self-assignment" occur? Because it also requires cost because it joins the new control branch. There is also an alternative: copy and swap technology. This technique is closely related to exceptional security and is described in detail in clause 29. Let's see how it's going to come true
Class Widget{public:void swap (const widget& RHS);//Exchange RHS and thiswidget& operator= (const widget& RHS) {Widget TMP (RHS);//assignment of a data swap (TMP)//Exchange return *this;//temporary variable will automatically destroy}int *p;};
If the assignment operator argument is a value pass, you do not need to create a new temporary variable and use the function arguments directly.
Class Widget{public:void swap (const widget& RHS);//Exchange RHS and thiswidget& operator= (const Widget RHS) {Swap (RHS) return *this;} int *p;};
This practice code is less readable, but moves the "copying action" from the function body to the "function parameter construction phase", where the compiler sometimes generates more efficient code (by moving the copying operation from the body of the function to construction of the parameter, it's fact that compiler can sometimes generate more efficient code.
Article 12: Do not forget each part of the object when copying itIn a class, there are two functions that can be copied to a copy object: A copy constructor and an assignment operator, collectively referred to as the copying function. As mentioned in clause 5, if we do not write the two functions ourselves, the compiler will help us implement both functions, and the compiler-generated version will make a copy of all the member variables of the object. The compiler-generated copying function is usually a shallow copy. can refer to here. If we implement the copying function ourselves, the compiler will no longer help us implement it. But the compiler won't help us check whether the copying function assigns values to every variable in the object. The following is a consumer class
Class Cutsomer{public:cutsomer () {name= "nobody";} Cutsomer (const cutsomer& RHS): Name (rhs.name) {cout<< "Customer Copy Ctor" <<ENDL; cutsomer& operator= (const cutsomer& RHS) {cout<< "Assign Operator" <<endl;name=rhs.name;return * this;} private:string name;};
There is no problem with such a copying function, but if you add a variable to the class, such as adding a phone number
Class cutsomer{......private:string name;string telphone;};
At this point the copying function does not change, even at the highest level of warning, the compiler will not error, but we do not copy the content.You can conclude that once you add a variable to a class, the copying function you write is also modified because the compiler does not remind you. In a derived class hierarchy, such bugs are more difficult to discover. If there is a priority client class, it inherits from the customer
Class Prioritycustomer:public Cutsomer{public:prioritycustomer () {cout<< "Prioritycustomer Ctor" <<ENDL;} Prioritycustomer (const prioritycustomer& RHS):p riority (rhs.priority) {cout<< "Prioritycustomer Copy Ctor" <<endl;} prioritycustomer& operator= (const prioritycustomer& RHS) {cout<< "Prioritycustomer Assign Operator" < <endl;priority=rhs.priority;return *this;} Private:int priority;};
In Prioritycustomer's copying function, only the contents of the Prioritycustomer section are copied, and the base class content is ignored. So how is the base class content part initialized?in a derived class, a constructor that is not initialized by a base class part is initialized by the base class default constructor (no default constructor will be an error). However, in the copy assignment operator, the default constructor for the base class is not called, because copy assignment simply assigns the object the value, not the initialization, and therefore does not call the constructor of the base class unless we display the call. The correct prioritycustomer copying function should be written like this:
Prioritycustomer (const prioritycustomer& RHS): Cutsomer (RHS), priority (rhs.priority) {cout<< " Prioritycustomer Copy Ctor "<<ENDL;} prioritycustomer& operator= (const prioritycustomer& RHS) {cout<< "Prioritycustomer Assign Operator" < <endl; Cutsomer::operator= (RHS);p Riority=rhs.priority;return *this;}
You can see that the copy constructor and assignment operators have similar code. However, two functions cannot be called from one another. The copy constructor constructs a non-existent object, and the assignment operator assigns a value to an existing object. Eliminate duplicate code by writing a private method, such as void Init (). Manipulate the repeating code in this function.
Effective C + + construction/destructor/assignment function: Clause 10-clause 12