Effective C + + learning notes (clause 11: handle "self assignment" in operator =)

Keywords: C++

Recently, I started to watch Effective C + +. In order to facilitate future review, I specially took notes. If I misunderstand the knowledge points in the book, please correct!!!

"Self assignment" means to assign values to yourself, such as:

class Widget{...};

Widget w;
w = w;		//Assign yourself a value

This looks stupid, but it's legal, so don't think it won't happen. In addition, you won't see the assignment action at a glance (e.g. w = w;), for example:

Widget arr[10] = {...};
arr[i] = arr[j];	//Potential self assignment, arr[i] and arr[j] store the same object or i == j

Widget w;
Widget* px = &w;
Widget* py = &w;

*px = *py;			//Potential self assignment, pointers px and py point to the same object

These unobtrusive self assignments are caused by aliases, where there is more than one method to refer to an object. If two objects are from the same inheritance system, they do not need to be declared as the same type, which may cause aliases, because the reference and pointer of the base class can point to a derived class object.

class Base {...};
class Derived:public Base {...};

void doSomething(const Base& rb,Derived* pd);//rb and * pd may actually be the same object

If you plan to manage resources yourself, you may fall into the trap of "accidentally releasing resources before stopping using them". Suppose you create a class to hold a pointer to a dynamically allocated bitmap:

class Bitmap{...}
class Widget{
  ...
private:
    Bitmap *bp;
};

Widget& Widget::operator=(const Widget& rhs){
  	delete pb;					//Release the current bitmap
  	pb = new Bitmap(*rhs.bp);		//Use bitmap rhs to copy a new bitmap
  	return *this;					//Return self reference
}

The problem of self assignment here is that * this (the destination of assignment) and rhs in the operator = function may be the same object. If it is the same object, delete releases not only the bitmap of the current object, but also the bitmap of the rhs object. The final result of the function is that the bitmap of the current object is released. The expected result is that nothing will change.

resolvent:

  • Check whether the passed in parameter is * this

    Widget& Widget::operator=(const Widget& rhs){
    	// Do an identity check first
    	if(this == &rhs)	return *this;          
    	// If you are not yourself, perform the following operations
      	delete pb;                   
      	pb = new Bitmap(*rhs.pb);
      	return *this;
    }
    

    This method solves the problem of self assignment, but it is not abnormally safe. If the new Bitmap causes an exception (whether it is due to insufficient memory during allocation or because the copy constructor of the Bitmap throws an exception), the Widget will eventually have a pointer to a deleted Bitmap.

  • Don't delete the bitmap before copying it

    Widget& Widget::operator=(const Widget& rhs){
    	Bitmap* pOrig = pb;			//Staging the original pb
        pb = new Bitmap(*rhs.pb);	//Make Pb point to a copy of * rhs.pb
      	delete pOrig;				//Delete the original pb
      	return *this;
    }
    

    If the new Bitmap throws an exception, pb (and which Widget it resides in) remains unchanged. Even without identity verification, it can handle self assignment, because our team made a copy of the in-situ map, then deleted the in-situ map, and finally pointed to the copied bitmap.

  • copy and swap

    class Widget{
      ...
    private:
        Bitmap *bp;
        
        void swap(Widget& rhs);	//Exchange * this data with rhs data
    };
    
    void Widget::swap(Widget& rhs){
        *bp = rhs.bp;
    }
    //Reference transfer method
    Widget& Widget::operator=(const Widget& rhs){
    	Widget temp(rhs);	//Copy a copy of rhs data to prevent self assignment
        swap(temp);			//Exchange * this data with rhs data
      	return *this;
    }
    //Value passing method: the parameter rhs is already a copy
    Widget& Widget::operator=(Widget rhs){
        swap(temp);			//Exchange * this data with rhs data
      	return *this;
    }
    

Note:

  • Handle the self assignment problem in the operator = function. The methods include "check whether the incoming parameter is * this", "don't delete it before copying the bitmap" and "copy first and then exchange"
  • If multiple parameters are used in the same function, it is also necessary to ensure that the function can work normally when these parameters point to the same object at the same time

Clause 12: don't forget every component when copying an object

Posted by eashton123 on Sat, 06 Nov 2021 08:54:50 -0700