What is boost's shared_ptr(shared_ptr<Y> const & r, T * p) used for?
-
05-07-2019 - |
Question
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.
Solution
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.
OTHER TIPS
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
instancep
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. Themake_shared
factory function presented later in this document can also be implemented using only the public interface ofshared_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 storesp
and shares ownership withr
.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 ofr
is destroyed. --end note.][Note: This constructor allows creation of an empty
shared_ptr
instance with a non-NULL stored pointer. --end note.]
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()));
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.
I have put shared_ptr's aliasing constructor in use in my little library:
http://code.google.com/p/infectorpp/ (just my simple IoC container)
The point is that since I needed a shared_ptr of known type to be returned from a polymorphic class (that does not know the type). I was not able to implicitly convert the shared_ptr to the type I needed.
In the file "InfectorHelpers.hpp" (line 72-99) you can see that in action for the type IAnyShared.
Aliasing constructor creates shared_ptr that does not delete the pointers they are actually pointing to, but they still increase the reference counter to the original object and that can be tremendously usefull.
Basically you can create a pointer to anything using aliasing constructor and threat it as a reference counter.
//my class
std::shared_ptr<T> ist;
int a; //dummy variable. I need its adress
virtual std::shared_ptr<int> getReferenceCounter(){
return std::shared_ptr<int>(ist,&a); //not intended for dereferencing
}
virtual void* getPtr(); //return raw pointer to T
now we have both "a reference counter" and a pointer to a istance of T, enough data to create something with the aliasing constructor
std::shared_ptr<T> aPtr( any->getReferenceCounter(), //share same ref counter
static_cast<T*>(any->getPtr()) ); //potentially unsafe cast!
I don't pretend to have invented this use for the aliasing constructor, but I never seen someone else doing the same. If you are guessing if that dirty code works the answer is yes.
For "shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));
"
I think it is not the recommended way using smart pointer.
The recommended way of doing this type conversion should be:
shared_ptr<B> b(a);
Since in Boost document it is mentioned that:
shared_ptr<T>
can be implicitly converted toshared_ptr<U>
whenever T* can be implicitly converted to U*. In particular,shared_ptr<T>
is implicitly convertible toshared_ptr<T> const
, toshared_ptr<U>
where U is an accessible base of T, and toshared_ptr<void>
.
In addition to that, we also have dynamic_pointer_cast which could directly do conversion on Smart Pointer object and both of these two methods would be much safer than the manually casting raw pointer way.