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. 

 

No comments:

Post a Comment