2

As i understand when assigned shared ptr should behave like:

a) if (--this->count == 0) { release this->count and this->obj }

b) this->count = r->count, this->obj = r->obj;

What boost does is just

shared_ptr & operator=( shared_ptr const & r ) BOOST_NOEXCEPT
{
    this_type(r).swap(*this);
    return *this;
}

So what is the magic behind swap and why this works?

Marco A.
  • 43,032
  • 26
  • 132
  • 246
silver_rocket
  • 418
  • 1
  • 4
  • 15

3 Answers3

7

a) if (--this->count == 0) { release this->count and this->obj }

No, a shared_ptr keeps two counts, one for the object and one for the control block containing the reference counts. You release this->obj when the first one reaches zero and releases this->count (and the second count) when the second one reaches zero.

b) this->count = r->count, this->obj = r->obj;

No, you're missing a reference count increment. Also, as Yakk's answer points out, you must either check for self-assignment or increment the right side first, to avoid incorrectly destroying the object on self-assignment.

So your understanding is incomplete/incorrect, but if you follow through the Boost code you'll see it does exactly the right thing. It increases the reference count on the r object, exchanges all the necessary values, then decreases the reference count on the original value of *this.

The step of increasing the reference count is already implemented in the copy constructor, so it re-uses that.

The step of exchanging all the necessary values is already implemented in the swap member, so it uses that.

The step of decreasing the reference counts (and freeing anything if required) is already done by the destructor, so it uses that.

This is excellent code re-use. The alternative would be to repeat all the same code as is found in the copy constructor, swap member and destructor, which would be redundant and error-prone.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
2

This is the copy-swap idiom. It isn't always efficient, but in this case it is.

To assign a ref count object, you first (a) increment the ref count of the right hand side, then (b) decrement the ref count of the left hand side and (c) store the state of the rhs on the lhs.

The order of (b) and (c) only matters if exceptions can occur, but (a) must happen before (b) in case there is a self-assignment (or equivalent).

The copy swap idiom instead does this:

  • (1) copy rhs to a temporary.
  • (2) swap lhs state with temporary (containing copy of rhs state)
  • (3) destroy temporary this does (a) in (1), then (c) in (2), then (b) in (3). And it does it in a particularly exception safe manner: the only time the code is in a 'strange' state is within a swap, and swap is easy to make exception-proof.

Copy swap can be used as a general easy to write assignment, but it doesn't reuse internal resources of the lhs, which can cost efficiency. This does not matter much in this case, as the lhs internal resources are pointers to shared state.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

By the end of the assignment operators scope (the closing }), the reference count for the original sptr object will be decreased by one, because r no longer points to it. Thus, it's functionally exactly what you're describing above.

Marcus Müller
  • 34,677
  • 4
  • 53
  • 94