Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is wrong with "checking for self-assignment" and what does it mean?

In Herb Sutter's book Exceptional C++ (1999), he has words in item 10's solution:

"Exception-unsafe" and "poor design" go hand in hand. If a piece of code isn't exception-safe, that's generally okay and can simply be fixed. But if a piece of code cannot be made exception-safe because of its underlying design, that almost always is a signal of its poor design.

Example 1: A function with two different responsibilities is difficult to make exception-safe.

Example 2: A copy assignment operator that is written in such a way that it must check for self-assignment is probably not strongly exception-safe either

What does he mean by the term "check for self-assignment"?

[INQUIRY]

Dave and AndreyT shows us exactly what "check for self-assignment" means. That's good. But the question is not over. Why does "check for self-assignment" hurts "exception safety"(according to Hurb Sutter)? If the caller tries to do self-assignment, that "check" works as if no assignment ever occurs. Does it really hurt?

[MEMO 1] In item 38 Object Identity later in Herb's book, he explains about self-assignment.

like image 301
Jimm Chen Avatar asked Aug 18 '12 01:08

Jimm Chen


People also ask

Why do we need to check for self-assignment in an assignment operator?

The general reason to check for self-assignment is because you destroy your own data before copying in the new one. This assignment operator structure is also not strongly exception safe.

What is self-assignment?

Definition of self-assignment : the act of assigning something (such as a task) to oneself working on/by self-assignment also : the task or work that is assigned Alexander came to Winged Foot in 1959 to shoot the U.S. Open. It was a self-assignment that resulted in a series of iconic photos … —


2 Answers

A question that's of greater importance in this case would be:


  • "What does it mean, when a you write function in a way, that requires you to check for self assignment???"

To answer my rhetorical question: It means that a well-designed assignment operator should not need to check for self-assignment. Assigning an object to itself should work correctly (i.e. have the end-effect of "doing nothing") without performing as explicit check for self-assignment.

For example, if I wanted to implement a simplistic array class along the lines of

class array {     // code...       int *data;     size_t n; }; 

...and came up with the following implementation of the assignment operator...

 array &array::operator =(const array &rhs)  {     delete[] data;      n = rhs.n;     data = new int[n];      std::copy_n(rhs.data, n, data);      return *this; }  




That implementation would be considered "bad" since it obviously fails in case of self-assignment.

In order to "fix" this, the developer has two options;

  1. He, or she, can either add an explicit self-assignment check
array &array::operator =(const array &rhs) {     if (&rhs != this) {         delete[] data;          n = rhs.n;         data = new int[n];          std::copy_n(rhs.data, n, data);     }     return *this; } 

  1. Or you can follow a "check-less" approach:
array &array::operator =(const array &rhs) {       size_t new_n = rhs.n;       int *new_data = new int[new_n];        std::copy_n(rhs.data, new_n, new_data);        delete[] data;        n = new_n;       data = new_data;        return *this; } 





The latter approach is better in a sense that it works correctly in self-assignment situations without making an explicit check. This implementation is far for perfect from a 'safety point of view', it is here to illustrate the difference between "checked" and "check-less" approaches to handling self-assignment. The later check-less implementation can be written more elegantly through the well-known copy-and-swap idiom.

This does not mean that you should avoid explicit checks for self-assignment. Such check do make sense from the performance point of view: there's no point in carrying out a long sequence of operations just to end up "doing nothing" in the end. But in a well-designed assignment operator such checks should not be necessary from the correctness point of view.

like image 188
AnT Avatar answered Oct 17 '22 07:10

AnT


From c++ core guide

Foo& Foo::operator=(const Foo& a)   // OK, but there is a cost {     if (this == &a) return *this;     s = a.s;     i = a.i;     return *this; } 

This is obviously safe and apparently efficient. However , what if we do one self-assignment per million assignments? That's about a million redundant tests (but since the answer is essentially always the same, the computer's branch predictor will guess right essentially every time). Consider:

Foo& Foo::operator=(const Foo& a)   // simpler, and probably much better {     s = a.s;     i = a.i;     return *this; } 

Note: The code above only apply to classes without pointer, for classes with pointer point to dynamic memory. Please refer to Ant's answer.

like image 40
camino Avatar answered Oct 17 '22 08:10

camino