Showing posts with label SmartPointers. Show all posts
Showing posts with label SmartPointers. Show all posts

Sunday, March 16, 2025

weak_ptr

Overview
A weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by shared_ptr. It must be converted to shared_ptr in order to access the referenced object.

Details
A weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by shared_ptr. It must be converted to shared_ptr in order to access the referenced object. A client holding a weak_ptr  basically checks if it's not expired(), later it's converted to a shared_ptr for accessing the resource.

Syntax
template <class T> class weak_ptr

member types
NameDescription
element_typefirst template parameter (T). The type of the managed object

Constructors
Commonly used constructors.
NameDescription
weak_ptr()default constructor.
Example:
weak_ptr<int>();
weak_ptr(weak_ptr&& x)move constructor.
Example:
weak_ptr<int> x;
weak_ptr<int> (move(x));
template <class U
weak_ptr
(weak_ptr<U>&& x)
move constructor. 
Example:
weak_ptr<int> (weak_ptr<int>());
weak_ptr
(const weak_ptr& x)
copy constructor. 
Example:
weak_ptr<int>x;
weak_ptr<int> (x);
template <class U>
weak_ptr
(const weak_ptr<U>& x)
copy constructor. 
Example:
weak_ptr<int> (weak_ptr<int>());
template <class U>
weak_ptr
(const shared_ptr<U>& x)
same as copy constructor. exception on thrown if x has expired.
Example:
weak_ptr<int>(shared_ptr<int>(new int, default_delete<int>()));
The example 15 demonstrates it. 

Methods
The following describes functionality in detail.
NameDescription
shared_ptr  <T> lock()                                                Returns a new shared_ptr that shares ownership of the resource that this weak_ptr also shares. If the weak_ptr is expired() or is empty. an empty shared_ptr is returned.

Example
shared_ptr<int> x(new int(10));
//create weak_ptr from shared_ptr
weak_ptr<int> w(x);
//check if weak_ptr is valid
if (!w.expired())
    //generate new shared_ptr and use it if it's valid
if (auto sp = w.lock())
        //prints 10
        cout << *sp;
bool expired()Checks if the resource associated no longer available. This happens when the shared_ptr associated with the resource is recycled.
Example
cout << boolalpha;
shared_ptr<int> *x  = new shared_ptr<int>(new int(10));
weak_ptr<int> w(*x);
//prints false
cout << w.expired() << endl; 
delete x;
//prints true
cout << w.expired() << endl; 
long use_count()Returns number of instances of shared_ptr sharing the same resource along with this weak_ptr. If the weak_ptr is empty, it's 0.

Example
shared_ptr<int> *x  = new shared_ptr<int>(new int(10));
weak_ptr<int> w(*x);
//prints 1
cout << w.use_count() << endl; 
delete x;
//prints 0
cout << w.use_count() << endl;
  1. template <class U>
      bool owner_before
    (const shared_ptr<U>& x)
  2. 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;
void reset()The weak_ptr becomes empty. use_count() becomes 0.
Example
cout << boolalpha;
shared_ptr<int> *x  = new shared_ptr<int>(new int(10));
weak_ptr<int> w(*x);
//prints 1
cout << w.use_count() << endl; 
w.reset();
//prints 0
cout << w.use_count() << endl; 
delete x;
weak_ptr& operator= (const weak_ptr& x)
weak_ptr& operator= (const weak_ptr<U>& x)
weak_ptr& operator= (const shared_ptr<U>& x)                                                                                                                                                         
Assignment operators.
  1. shares the resource shared by weak_ptr x.
  2. Same as 1 
  3. shares the resource co owned by shared_ptr x. 
The example 16 demonstrates it. 

Usage Examples
Ticketing Application
An airline ticketing application holds several time bound discounted ticket offers that expire after certain period.
The example 17  depicts a solution. 
As seen in its console output, main thread creates shared_ptr based on an time bound offer. Later it creates a thread to monitor and reset the shared_ptr after the validity period of 8 seconds, thus  all weak_ptrs derived from it will also expire.
Later main thread creates a child thread to fulfil the offer. A weak_ptr is created from the shared_ptr object and passed to this thread. The child thread waits for 5 seconds. If the weak_ptr has not expired, the offer will be fulfilled. The offer is fulfilled.
The main thread again it creates  another child thread to fulfil the offer. This time the offer gets expired and the offer is not fulfilled.

Doubly linked list
weak_ptr can be used to remove cyclic dependency where two objects hold reference to each other. From previous example of doubly linked list,  shared_ptr* can be replaced by weak_ptr, as shown below.
struct doublelist
{
    struct node;
    using LLNODE = shared_ptr<node>;
    using LLNODEptr = weak_ptr<node>;
struct node { T data; LLNODEptr prev; LLNODE next; }; LLNODE head; LLNODEptr tail; }
The example 18 depicts the solution. 

 

Wednesday, March 12, 2025

shared_ptr

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

member types
NameDescription
element_typefirst template parameter (T). The type of the managed object

Constructors
Commonly used constructors. Some support custom allocators.
NameDescription
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);
The example 5 demonstrates it. 

Methods
The following describes functionality in detail.
NameDescription
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;
  1. template <class U>
      bool owner_before
    (const shared_ptr<U>& x)
  2. 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;
  1. template <class U>
    void reset (U* p)
  2. template <class U, class D>
    void reset (U* p, D del)
  3. void reset()
Destroys the resource it's currently holding by calling deleter and releases the resource. Optionally new resource can be set to it.
  1. Replaces with a new value which is compatible with its type T.
  2. Same as 1 and also accepts new deleter
  3. 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];
  1. shared_ptr& operator=
    (const shared_ptr& x) 
  2. template <class U>
    shared_ptr& operator=
    (const shared_ptr<U>& x)
  3. shared_ptr& operator=
    (shared_ptr&& x)
  4. template <class U>
    shared_ptr& operator=
    (shared_ptr<U>&& x)
  5. template <class U>
    shared_ptr& operator=
    (auto_ptr<U>&& x)
  6. 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 example 6 demonstrates it. 

External Methods
The following describes relational operators.
NameDescription
  1. template <class T, class U>
    bool operator ==
    (const shared_ptr<T>& lhs,
    const shared_ptr<U>& rhs)
  2. template <class T>
    bool operator ==
    (const shared_ptr<T>& lhs, nullptr_t)
  3. template <class T>
    bool operator ==
    (nullptr_tconst shared_ptr<T>& rhs) 
Equality operator.
  1. template <class T, class U>
    bool operator !=
    (const shared_ptr<T>& lhs,
    const shared_ptr<U>& rhs)
  2. template <class T>
    bool operator !=
    (const shared_ptr<T>& lhs, nullptr_t)
  3. template <class T>
    bool operator !=
    (nullptr_tconst shared_ptr<T>& rhs) 
Inequality operator.
  1. template <class T, class U>
    bool operator >
    (const shared_ptr<T>& lhs,
    const shared_ptr<U>& rhs)
  2. template <class T>
    bool operator >
    (const shared_ptr<T>& lhs, nullptr_t)
  3. template <class T>
    bool operator >
    (nullptr_tconst shared_ptr<T>& rhs) 
Greater than operator.
  1. template <class T, class U>
    bool operator >=
    (const shared_ptr<T>& lhs,
    const shared_ptr<U>& rhs)
  2. template <class T>
    bool operator >=
    (const shared_ptr<T>& lhs, nullptr_t)
  3. template <class T>
    bool operator >=
    (nullptr_tconst shared_ptr<T>& rhs) 
Greater than or equal to operator.
  1. template <class T, class U>
    bool operator <
    (const shared_ptr<T>& lhs,
    const shared_ptr<U>& rhs)
  2. template <class T>
    bool operator <
    (const shared_ptr<T>& lhs, nullptr_t)
  3. template <class T>
    bool operator <
    (nullptr_tconst shared_ptr<T>& rhs) 
Less than operator.
  1. template <class T, class U>
    bool operator <=
    (const shared_ptr<T>& lhs,
    const shared_ptr<U>& rhs)
  2. template <class T>
    bool operator <=
    (const shared_ptr<T>& lhs, nullptr_t)
  3. template <class T>
    bool operator <=
    (nullptr_tconst shared_ptr<T>& rhs) 
Less than or equal to operator.

The following describes ostream operator.
NameDescription
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;
The example 7 demonstrates its usage.

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);
The example 8 demonstrates its usage.

shared pointer casting()
These are similar to cast functions such as staticdynamic 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();
The example 9 demonstrates its usage.

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
NameDescription
result_type bool indicating result of the operation.
first_argument_typefirst template parameter (Ptr). This can be either shared_ptr<T> or weak_ptr<T>.
second_argument_typesecond 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.
NameDescription
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
  1. bool operator()
    (const shared_ptr<T>& lhs,
    const weak_ptr<T>& rhs )
  2. bool operator()
    (const weak_ptr<T>& lhs,
    const shared_ptr<T>& rhs )
specialization of shared_ptr and weak_ptr.
The example 10 demonstrates its usage.

enable_shared_from_this
Consider the following scenario.
  1. An instance of shared_ptr x is created from class C. 
  2. class C defines a method getptr() that returns  shared_ptr from self. 
  3. 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.
This is depicted in example 11.

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

Constructors
Commonly used constructors. Some support custom allocators.
NameDescription
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.
NameDescription
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();
The example 12 demonstrates its usage.

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; }
The example 13 depicts a solution. 

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.
This  is implemented in the  example 14
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.