Copy boost::shared_ptr

16,085

Solution 1

Well, if you have a shared_ptr and you assign it to another shared_ptr, those two shared pointers will share ownership of the object - meaning the reference counting for the pointed object 's ownership will be increased by one.

In fact, in the above line, you do not need to construct a temporary shared pointer at all. This is enough:

passed_ptr = cached_ptr;

Anyway, copy-constructing a shared_ptr basically follows the same logic: you start with a shared pointer, and you end up with two shared pointers that co-own the same object (meaning that the object will be destroyed only when both of those shared pointers get destroyed). So when you do this:

passed_ptr = shared_ptr(cached_ptr);

You actually start with one shared pointer (cached_ptr) to a given object, then create a temporary one (that brings the reference count to 2) which gets in turn assigned to passed_ptr (bringing the reference count to 3), and eventually gets destroyed (bringing the reference count back to 2).

On the other hand, if what you want is to have passed_ptr as a shared pointer to a copy of the object being pointed to by cached_ptr, then you should rather do (supposing Data is copyable, of course):

passed_ptr = boost::make_shared<Data>(*cached_ptr);

Or, alternatively:

passed_ptr.reset(new Data(*cached_ptr));

Solution 2

Okay. Let's see if we can talk about this.

std::shared_ptr<Data> x = std::make_shared<Data>();
std::shared_ptr<Data> y = x;

assert(x.get() == y.get());

If I change something about what x points to, that would change information about y as well. Because they point to the same thing.

x->member = 3;
assert(x->member == 3);
assert(y->member == 3);

I can change what x points to without changing what y points to.

x = std::make_shared<Data>();
assert(x.get() != y.get());

If I do this, then changes to x are not reflected to in y. Because they point to different things.

x->member = 4;
assert(x->member == 4);
assert(y->member != 4);

If I want to make a copy of the contents of x, and store that in y, then I need to create a new shared object.

y = std::make_shared<Data>(*x);

At this point, both x and y have that member variable set to 4. Because x had it set, and we created *y with the contents of *x.

assert(x->member == 4);
assert(y->member == 4);

But, because x and y are pointing to different things in memory, we can change one of them.

assert(x.get() != y.get();
x->member = 3;
assert(x->member == 3);
assert(y->member == 4);

If I was to pass a shared_ptr to a function, then it's just like the first case.

void func(std::shared_ptr<Data> z) {
    assert(x.get() == z.get());
}

func(x);

I can also release the contents of a particular shared_ptr by using reset(). This will cause the value that the shared_ptr is pointing at to become NULL.

x.reset();
assert(x.get() == NULL);

With regards to const correctness, it's a bit weird.

const std::shared_ptr<Data> x;
x.reset(); // Fails because the shared_ptr is const.
x->member = 3; // Succeeds because Data is not const.

std::shared_ptr<const Data> x;
x.reset(); // Succeeds because the shared_ptr is not const.
x->member = 3; // Fails because Data is const.

Solution 3

The usual convention would be to pass a const shared_ptr:

bool
someWork( data_ptr const& passed_ptr )
{
    //  ...
}

This allows using the pointer (including copying and converting it), but not modifying it.

If you want to change what the pointer points to at the call site, then you have to pass a non-const reference. The simplest policy here is, IMHO, to assign the new value to it, but you can also call the reset function.

As to how it works: when you pass the pointer to a reference parameter, you simply give the called function access to the original pointer. Under the hood, it's very much like a pointer which is systematically dereferences. And you seem to be confusing the pointer with what it points to; it is what it points to that is shared, not the pointer itself. The pointer itself behaves exactly like any other C++ object.

Share:
16,085
Thalia
Author by

Thalia

Junior developer exploring image processing and driver development

Updated on June 17, 2022

Comments

  • Thalia
    Thalia almost 2 years
    typedef boost::shared_ptr<SomeData> data_ptr;
    data_ptr cached_ptr;   // class member 
    bool someWork(data_ptr& passed_ptr)
    {
      // must copy passed_ptr = cached_ptr under some conditions
      // without pointing at the same memory 
      // I saw somewhere that I should do 
      // passed_ptr.reset(new SomeData(???))
      // I don't have a "reset" on passed_ptr
    }
    

    I looked at documentation;

    copy and converting constructors

    shared_ptr(shared_ptr const & r); // never throws
    template<class Y> shared_ptr(shared_ptr<Y> const & r); // never throws
    Requires: Y* should be convertible to T*.
    
    Effects: If r is empty, constructs an empty shared_ptr; otherwise,
             constructs a shared_ptr that shares ownership with r.
    

    I don't know how that works - is it like this ?

    passed_ptr = shared_ptr(cached_ptr);
    

    ? Where would the const go ? And what does it mean that they share ownership ? Then it is not a copy, if I modify "passed_ptr", the change will affect "cached_ptr" ?

    I can't find examples... Please help.

    Thank you.