views:

466

answers:

4

boost::shared_ptr has an unusual constructor

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p);

and I am a little puzzled as to what this would be useful for. Basically it shares ownership with r, but .get() will return p. not r.get()!

This means you can do something like this:

int main() {
    boost::shared_ptr<int> x(new int);
    boost::shared_ptr<int> y(x, new int);

    std::cout << x.get() << std::endl;
    std::cout << y.get() << std::endl;

    std::cout << x.use_count() << std::endl;
    std::cout << y.use_count() << std::endl;
}

And you will get this:

0x8c66008
0x8c66030
2
2

Note that the pointers are separate, but they both claim to have a use_count of 2 (since they share ownership of the same object).

So, the int owned by x will exist as long as x or y is around. And if I understand the docs correct, the second int never gets destructed. I've confirmed this with the following test program:

struct T {
    T() { std::cout << "T()" << std::endl; }
    ~T() { std::cout << "~T()" << std::endl; }
};

int main() {
    boost::shared_ptr<T> x(new T);
    boost::shared_ptr<T> y(x, new T);

    std::cout << x.get() << std::endl;
    std::cout << y.get() << std::endl;

    std::cout << x.use_count() << std::endl;
    std::cout << y.use_count() << std::endl;
}

This outputs (as expected):

T()
T()
0x96c2008
0x96c2030
2
2
~T()

So... what is the usefulness of this unusual construct which shares ownership of one pointer, but acts like another pointer (which it does not own) when used.

+17  A: 

It is useful when you want to share a class member and an instance of the class is already a shared_ptr, like the following:

struct A
{
  int *B; // managed inside A
};

shared_ptr<A>   a( new A );
shared_ptr<int> b( a, a->B );

they share the use count and stuff. It is optimization for memory usage.

leiz
a good answer. clearly, in this example, we would want to keep `a`'s object around as long as `b` is around. I think we have a winner.
Evan Teran
David Rodríguez - dribeas
+2  A: 

You might have a pointer to some driver or a lower level api's data structure that may allocate additional data by its lower level api or other means. In this case it might be interesting to increase the use_count but return the additional data if the first pointer owns the other data pointers.

piotr
+5  A: 

To expand on leiz's and piotr's answers, this description of shared_ptr<> 'aliasing' is from a WG21 paper, "Improving shared_ptr for C++0x, Revision 2":

III. Aliasing Support

Advanced users often require the ability to create a shared_ptr instance p that shares ownership with another (master) shared_ptr q but points to an object that is not a base of *q. *p may be a member or an element of *q, for example. This section proposes an additional constructor that can be used for this purpose.

An interesting side effect of this increase of expressive power is that now the *_pointer_cast functions can be implemented in user code. The make_shared factory function presented later in this document can also be implemented using only the public interface of shared_ptr via the aliasing constructor. Impact:

This feature extends the interface of shared_ptr in a backward-compatible way that increases its expressive power and is therefore strongly recommended to be added to the C++0x standard. It introduces no source- and binary compatibility issues. Proposed text:

Add to shared_ptr [util.smartptr.shared] the following constructor:

template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );

Add the following to [util.smartptr.shared.const]:

template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );

Effects: Constructs a shared_ptr instance that stores p and shares 
ownership with r.

Postconditions: get() == p && use_count() == r.use_count().

Throws: nothing.

[Note: To avoid the possibility of a dangling pointer, the user 
of this constructor must ensure that p remains valid at least 
until the ownership group of r is destroyed. --end note.]

[Note: This constructor allows creation of an empty shared_ptr
instance with a non-NULL stored pointer. --end note.]
Michael Burr
+1  A: 

You can also use this to keep dynamic casted pointers, i.e.:

class A {};
class B: public A {};

shared_ptr<A> a(new B);
shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));
another interesting use that clearly falls into the category of "aliasing". Good point.
Evan Teran