Overview
shared_ptr are useful when a resource such as a piece of memory or a file handle can be collectively shared by multiple owners.
Details
Sometimes a resource needs to be shared across without duplication. An unique_ptr discussed above cannot be used in such cases.
For example, a chat server needs to share a message its clients without duplication.
shared_ptr addresses this issue. Unlike a unique_ptr, the underlying resource is collectively owned by a set of shared_ptrs. The underlying resource is released when the last instance of shared_ptr goes out of scope.
shared_ptr
Syntax
template <class T> class shared_ptr
Name | Description |
---|
element_type | first template parameter (T). The type of the managed object
|
Commonly used constructors. Some support custom allocators.
Name | Description |
---|
shared_ptr() | default constructor. use_count() is 0. Example:
shared_ptr<int>() |
shared_ptr (nullptr_t) | same as default constructor. Example:
shared_ptr<int>(nullptr) |
template <class U> shared_ptr (U* p) | takes ownership of p and use default deleter. use_count() is 1. Example:
shared_ptr<int>(new int) |
template <class U, class D> shared_ptr (U* p, D del) | takes ownership of p and use deleter del. use_count() is 1. Example:
auto d = default_delete<int>();
shared_ptr<int> (new int, d); |
template <class D> shared_ptr (nullptr_t, D del) | same as default but uses deleter del. use_count() is 0. Example:
auto d = default_delete<int>();
shared_ptr<int> (nullptr, d); |
shared_ptr (const shared_ptr& x) | copy constructor. shares pointer and deleter from x. increments use_count(). Example:
auto d = default_delete<int>();
shared_ptr<int> x(new int, d);
shared_ptr<int> (x);
|
template <class U> shared_ptr (const shared_ptr<U>& x) | copy constructor. shares pointer and deleter from x. Example:
auto d = default_delete<int>();
shared_ptr<int> (shared_ptr<int> (new int, d)); |
shared_ptr (shared_ptr&& x) | move constructor. move pointer and deleter from x. x becomes empty. Example:
shared_ptr<int> x(new int, default_delete<int>());
shared_ptr<int> (move(x)) |
template <class U> shared_ptr (const shared_ptr<U>&& x) | move constructor. move pointer and deleter from x. x becomes empty. Example:
auto d = default_delete<int>();
shared_ptr<int>(shared_ptr<int> (new int, d));
|
template <class U> shared_ptr (const weak_ptr<U>& x) | same as copy constructor. exception on thrown if x has expired. Example:
shared_ptr<int> x (new int);
weak_ptr<int> w(x);
shared_ptr<int> y (w); |
template <class U> shared_ptr (auto_ptr<U>&& x) | takes ownership from x. uses default deleter and use_count() is 1. Example:
shared_ptr<int> (auto_ptr<int> (new int)); |
template <class U, class D> shared_ptr (unique_ptr<T,D>&& x) | takes ownership from x and use_count() is 1. Example:
shared_ptr<int> (unique_ptr<int> (new int, default_delete<int>())); |
template <class U> shared_ptr (const shared_ptr<U>& x, element_type *p) | Unlike shared_ptr constructors discussed above, an aliasing constructor co owns internal member object of shared_ptr x. It also increments its use_count(). In the example below, the owned object will be share_ptr x and the stored object will be x->data. In this case the get() will return stored object. Example:
struct S {int data;};
shared_ptr<S> x(new S);
shared_ptr<int> (x, x->data); |
Methods
The following describes functionality in detail.
Name | Description |
---|
element_type* get() | Returns the pointer of the resource it's holding without relinquishing the ownership. Example
auto sp{shared_ptr<int>(new int(10))};
//prints 10
cout << *sp.get() << endl; |
bool unique() | Checks if use_count() is 1. Example
cout << boolalpha;
auto sp{shared_ptr<int>(new int(10))};
//prints true
cout << sp.unique() << endl;
//prints false
cout << shared_ptr<int>(sp).unique() << endl; |
long int use_count() | Returns number of instances of shared_ptr sharing the resource. Example
auto sp{shared_ptr<int>(new int(10))};
//prints 1
cout << sp.use_count() << endl;
//prints 2
cout << shared_ptr<int>(sp).use_count() << endl; |
- template <class U>
bool owner_before (const shared_ptr<U>& x) - template <class U>
bool owner_before (const weak_ptr<U>& x)
| Applies to alias based constructs. When two shared_ptrs or weak_ptrs are compared using operator <, value based comparison is applied which compares the stored objects. Whereas owner_before() uses owner_based comparison that compares the owner share_ptr objects instead. Two of these aliased shared_ptr or weak_ptr are considered equivalent (i.e., this function returns false no matter the order of the operands) if they both share ownership, or they are both empty.
Example
cout << boolalpha; //1
auto sp{shared_ptr<int>{new int{10}}};
auto sp2{shared_ptr<int>{sp,new int {20}}};
auto sp3{shared_ptr<int>{sp,new int {30}}};
//prints *sp2 = 20 *sp3 = 30
cout << "*sp2 = " << *sp2 << " *sp3 = " << *sp3 << endl;
//prints true
cout << (sp2 < sp3) << endl;
//prints false
cout << sp2.owner_before(sp3) << endl;
cout << sp3.owner_before(sp2) << endl;
cout << endl;
//2
auto wp2{weak_ptr<int>{sp2}};
auto wp3{weak_ptr<int>{sp3}};
//prints false
cout << wp2.owner_before(wp3) << endl;
//prints false
cout << wp3.owner_before(wp2) << endl;
|
- template <class U>
void reset (U* p) - template <class U, class D>
void reset (U* p, D del) - void reset()
| Destroys the resource it's currently holding by calling deleter and releases the resource. Optionally new resource can be set to it.
- Replaces with a new value which is compatible with its type T.
- Same as 1 and also accepts new deleter
- Becomes empty
Example
struct product{int id;};
template<>
struct std::default_delete<product>
{
void operator()(product *p)
{
cout << "deleting product id: " << p->id << endl;
delete p;
}
};
shared_ptr<product> sp;
//1
sp.reset(new product(20));
//2
sp.reset(new product(30),default_delete<product>());
//prints:deleting product id: 30
//3
sp.reset(); |
operator bool() | Can be used to check if the use_count() is 1 or more. Example
shared_ptr<int> sp;
cout << boolalpha;
//prints false
cout << (bool)sp << endl;
sp.reset(new int);
//prints true
cout << (bool)sp << endl; |
element_type& operator *() | Object dereferencing. Example
shared_ptr<int> sp(new int(20));
//prints 20
cout << *sp; |
element_type* operator ->() | Member dereferencing. Example
shared_ptr<pair<int,int>> sp(new pair<int,int> {10,20});
//prints 20
cout << sp->second; |
element_type& operator [] (size_t index) | Array accessing index operator. Available only in specialized constructs. Example
shared_ptr<int[]> sp(new int[10]);
sp[5]=20;
//prints 20
cout << sp[5]; |
- shared_ptr& operator=
(const shared_ptr& x) - template <class U>
shared_ptr& operator= (const shared_ptr<U>& x) - shared_ptr& operator=
(shared_ptr&& x) - template <class U>
shared_ptr& operator= (shared_ptr<U>&& x) - template <class U>
shared_ptr& operator= (auto_ptr<U>&& x) - template <class U, class D>
shared_ptr& operator= (unique_ptr<U,D>&& x)
| Assignment Operator. Example
shared_ptr<int> sp (new int(10));
shared_ptr<int> sp2;
//1
sp2=sp;
//prints sp = 10 sp2= 10
cout << "sp = " << *sp << " sp2= " << *sp2 << endl;
//2
sp2 = shared_ptr<int>(new int(20));
//prints sp2= 20
cout << "sp2= " << *sp2 << endl;
//3
sp2 = move(sp);
//prints sp2= 10
cout << "sp2= " << *sp2 << endl;
//4
sp2 = move(shared_ptr<int>(new int(30)));
//prints sp2= 30
cout << "sp2= " << *sp2 << endl;
//6
sp2 = move(unique_ptr<int>(new int(40), default_delete<int>()));
//prints sp2= 40
cout << "sp2= " << *sp2 << endl;
|
The following describes relational operators.
Name | Description |
---|
- template <class T, class U>
bool operator == (const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) - template <class T>
bool operator == (const shared_ptr<T>& lhs, nullptr_t) - template <class T>
bool operator == (nullptr_t, const shared_ptr<T>& rhs)
| Equality operator. |
- template <class T, class U>
bool operator != (const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) - template <class T>
bool operator != (const shared_ptr<T>& lhs, nullptr_t) - template <class T>
bool operator != (nullptr_t, const shared_ptr<T>& rhs)
| Inequality operator. |
- template <class T, class U>
bool operator > (const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) - template <class T>
bool operator > (const shared_ptr<T>& lhs, nullptr_t) - template <class T>
bool operator > (nullptr_t, const shared_ptr<T>& rhs)
| Greater than operator. |
- template <class T, class U>
bool operator >= (const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) - template <class T>
bool operator >= (const shared_ptr<T>& lhs, nullptr_t) - template <class T>
bool operator >= (nullptr_t, const shared_ptr<T>& rhs)
| Greater than or equal to operator. |
- template <class T, class U>
bool operator < (const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) - template <class T>
bool operator < (const shared_ptr<T>& lhs, nullptr_t) - template <class T>
bool operator < (nullptr_t, const shared_ptr<T>& rhs)
| Less than operator. |
- template <class T, class U>
bool operator <= (const shared_ptr<T>& lhs, const shared_ptr<U>& rhs) - template <class T>
bool operator <= (const shared_ptr<T>& lhs, nullptr_t) - template <class T>
bool operator <= (nullptr_t, const shared_ptr<T>& rhs)
| Less than or equal to operator. |
The following describes ostream operator.
Name | Description |
---|
template <class charT, class traits, class T>
basic_ostream<charT,traits>& operator<< (basic_ostream<charT,traits>& os, const shared_ptr<T>& x)
| Insert stored pointer into output stream.
Example
//prints 0x33f81f80
cout << make_shared<int>(10);
return 0; |
make_shared()
make_shared() template function enables creation of shared_ptr and initializing it by passing by passing argument list.
Syntax
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
Example
auto sp = make_shared<pair<int,int>>(10,20);
//prints 10 20
cout << sp->first << " " << sp->second << endl;
allocate_shared()
Similar to make_shared(), allocate_shared() template function enables creation of shared_ptr and initializing it by passing by passing argument list. However it uses a custom allocator.
Syntax
template <class T, class Alloc, class... Args>
shared_ptr<T> allocate_shared (const Alloc& alloc, Args&&... args);
Example
allocator<int> alloc;
auto sp = allocate_shared<int> (alloc,10);
shared pointer casting()
These are similar to cast functions such as static, dynamic and const except applies to resource owned by a shared_ptr.
Syntax
template <class T, class U> shared_ptr<T> static_pointer_cast (const shared_ptr<U>& sp)
template <class T, class U> shared_ptr<T> dynamic_pointer_cast (const shared_ptr<U>& sp)
template <class T, class U> shared_ptr<T> const_pointer_cast (const shared_ptr<U>& sp)
When applied it creates new shared_ptr<T> object after applying cast on shared_ptr<U> . The use_count() is incremented.
Example
struct base
{
void print() {cout << "base" << endl;}
//needed for dynamic_cast
virtual ~base(){}
};
struct derived:public base
{
//const needed for const cast
void print() const {cout << "derived" << endl;}
//needed for dynamic cast
virtual ~derived(){}
} d;
auto spd = make_shared<derived>(d);
auto spb = static_pointer_cast<base>(spd);
//prints 2
cout << spb.use_count() << endl;
//prints base
spb->print();
auto spd2 = dynamic_pointer_cast<derived>(spb);
//prints 3
cout << spd2.use_count() << endl;
//prints derived
spd2->print();
auto spd3 = const_pointer_cast<const derived>(spd2);
//prints 4
cout << spd3.use_count() << endl;
//prints derived
spd3->print();
owner_less
As discussed earlier, aliasing constructs and owner_before() of use owner based comparison. owner_less define functor objects that use owner based comparison while comparing shared_ptr or weak_ptr.
This is a replacement for less (or operator<) to be used for these types when sorting needs to be based on their owned pointer instead of their stored pointer (which is what is compared by operator<).
Syntax
//Ptr The type of the managed pointers to be ordered according to owned resource, aliased as member types first_argument_type and second_argument_type.
template <class Ptr>
struct owner_less
//T The type of object pointed by the managed pointer type.
//specialization of shared_ptr
template <class T>
struct owner_less<shared_ptr<T>>
//specialization of weak_ptr
template <class T>
struct owner_less<weak_ptr<T>>
member types
Name | Description |
---|
result_type | bool indicating result of the operation.
|
first_argument_type | first template parameter (Ptr). This can be either shared_ptr<T> or weak_ptr<T>. |
second_argument_type | second template parameter (Ptr). This can be either shared_ptr<T> or weak_ptr<T>. |
methods
The comparison is provided by overloaded operator () function. Returns true if the first argument is considered to go before the second argument in a strict weak ordering based on their owned pointers.
The function uses shared_ptr::owner_before and/or weak_ptr::owner_before to determine this order.
Name | Description |
---|
bool operator() (const shared_ptr<T>& lhs, const shared_ptr<T>& rhs ) | specialization of shared_ptr |
bool operator() (const weak_ptr<T>& lhs, const weak_ptr<T>& rhs ) | specialization of weak_ptr |
- bool operator()
(const shared_ptr<T>& lhs, const weak_ptr<T>& rhs ) - bool operator()
(const weak_ptr<T>& lhs, const shared_ptr<T>& rhs )
| specialization of shared_ptr and weak_ptr. |
enable_shared_from_this
Consider the following scenario.
- An instance of shared_ptr x is created from class C.
- class C defines a method getptr() that returns shared_ptr from self.
- Another instance of shared_ptr y is created from x.
struct C
{
shared_ptr<C> getptr() { return shared_ptr<C>(this); }
};
auto x = make_shared<C>();
//prints 1
cout << x.use_count();
auto y = x->getptr();
//prints 1
cout << y.use_count();
Even though it's legal, it has a side effect. The use_count() of shared_ptr x is not incremented. This leads to unexpected situations such as double delete leading to crash.
To get around this issue, enable_shared_from_this class is introduced. All the classes that intend to create a shared_ptr from self should derive from this class.
syntax
template< class T > class enable_shared_from_this
ConstructorsCommonly used constructors. Some support custom allocators.
Name | Description |
---|
enable_shared_from_this() | default constructor. Example
struct C:public enable_shared_from_this<C>
{
shared_ptr<C> getptr() { return shared_from_this(); }
}; |
enable_shared_from_this (const enable_shared_from_this&) | copy constructor. |
Methods
It provides a single method, shared_from_this() to return a shared_ptr with use_count() increment.
Name | Description |
---|
shared_ptr <T> shared_from_this() | Constructs and returns a shared_ptr object pointing to *this and sharing ownership with existing shared_ptr objects.
Example
struct C:public enable_shared_from_this<C>
{
shared_ptr<C> getptr() { return shared_from_this(); }
};
auto x = make_shared<C>();
//prints 1
cout << x.use_count();
auto y = x->getptr();
//prints 2
cout << y.use_count();
|
Usage Example
linked list
As implemented using unique_ptr, a double linked list can also be implemented using shared_ptr .
The same strategy also implies here to avoid double referencing as shown below.
struct doublelist
{
struct node;
using LLNODE = shared_ptr<node>;
using LLNODEptr = shared_ptr<node>*;
struct node
{
T data;
LLNODEptr prev;
LLNODE next;
};
LLNODE head;
LLNODEptr tail;
}
linked list
A chat server needs to share a message with its clients without duplication.
The solution is to use shared_ptr for sharing the message across the clients.
As seen in its console output, a chaat server publishes a message with its 3 clients. Instead of duplicating the message, it's forwarded as shared_ptr to the clients. After all the instances of shared pointer are destroyed, deleter is called.