std::weak_ptr

std::weak_ptr

  • std::weak_ptr is'nt a standalone smart pointer. It's an augmentation of std::shared_ptr. 
  • It do not contribute to the reference count of the managed object they refer to.
  • By using just the raw pointer, it is impossible to know if the referenced data has been deallocated or not.
  • std::weak_ptr provides a "expired" API to check if the shared_ptr it was pointing to is expired.
In a nutshell -

The weak_ptr class template stores a “weak reference” to an object that’s already managed by a shared_ptr. To access the object, a weak_ptr can be converted to a shared_ptr using the shared_ptr constructor or the member function lock. When the last shared_ptr to the object goes away and the object is deleted, the attempt to obtain a shared_ptr from the weak_ptr instances that refer to the deleted object will fail: the constructor will throw an exception of type boost::bad_weak_ptr, and weak_ptr::lock will return an empty shared_ptr.


auto sPtr = std::make_shared<Base>(); 
// The reference count of sPtr is 1.

std::weak_ptr<Base> wPtr(sPtr); 
// wPtr points to same Base as sPtr. Reference count remains same

sPtr = nullptr;
// Referece count is 0 now, and the Base is destroyed, wPtr now dangles.

std::weak_ptr that dangles are said to be expired.
if(wPtr.expired()
  • std::weak_ptr lacks dereferencing operations (*, ->), to point to the shared_ptr it was referring to.

There are two ways to get the shared_ptr from weak_ptr

  • One form is std::weak_ptr::lock - which returns a std::shared_ptr , the std::shared_ptr is null if the std::weak_ptr is expired.  
        std::shared_ptr<Base> sPtr1 = wPtr.lock();
        // If wPtr is expired, sPtr1 is null
  • The other form is the std::shared_ptr constructor taking a std::weak_ptr as an argument. If passed std::weak_ptr has expired, an exception is thrown.
        std::shared_ptr<Base> sPtr3(wPtr);   
        // If wPtr is expired, throw std::bad_weak_ptr


Usage of std::weak_ptr.

  • To solve dangling pointer.
  • To remove circular reference.

Solve dangling pointer.

// Check if the pointer is expired, then get assocaited pointer and print the contents.


Another example to check for the dangling pointer.


#include <iostream>
#include <memory>

int main()
{
    // PROBLEM: ref will point to undefined data!

    int* ptr = new int(10);
    int* ref = ptr;
    delete ptr;

    // Check expired() or lock() to determine if pointer is valid

    // empty definition
    std::shared_ptr<int> sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr<int> weak1 = sptr;

    // deletes managed object, acquires new pointer
    sptr.reset(new int);
    *sptr = 5;

    // get pointer to new data without taking ownership
    std::weak_ptr<int> weak2 = sptr;

    // weak1 is expired!
    if(auto tmp = weak1.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak1 is expired\n";

    // weak2 points to new data (5)
    if(auto tmp = weak2.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak2 is expired\n";
}


/*
Output- 

weak1 is expired
5

*/

Remove circular reference.

Consider the below example - 

class B;
class A
{
   public:
        A() : sPtrToB(nullptr) { };
        ~A()
        {
             cout<<"A destrcutor\n";
        }
        std::shared_ptr<B> sPtrToB;
};

class B
{
      public:
           B() : sPtrToA(nullptr) { };
           ~B()
           {
               cout<<"B desctructor\n"
           }
           std::shared_ptr<A> sPtrToA;
};

int main()
{
     shared_ptr<A> sPtrA ( new A ); // Ref Count of sPtrA = 1
     shared_ptr<B> sPtrB ( new B ); // Ref Count of sPtrB = 1

     sPtrA->sPtrToB = sPtrB; // Ref Count of sPtrA = 2
     sPtrB->sPtrToA = sPtrA; // Ref Count of sPtrB = 2
}

Ref count of sPtrA and sPtrB is 1.

The above code has cyclic reference. class A holds a shared pointer to B and class B holds a shared pointer to A. The resource associated with both sPtrToA and sPtrToB are not released. 

It can be solved using having a std::weak_ptr as data member and as reference to another class.

class A
{
public:
    int _a;
    weak_ptr<B> sPtrToB;
    
    void printB()
    {
       if(sPtrToB->expired())
       {
            cout<<"Expired....!!!\n"
       }
       else
       {
            cout<<sPtrToB->lock()->_b<<endl;;
       }
    }
};

class B
{
public:
     int _b;
     weak_ptr<A> sPtrToA;
  
     

    void printA()
    {
       if(sPtrToA->expired())
       {
            cout<<"Expired....!!!\n"
       }
       else
       {
            cout<<sPtrToA->lock()->_a<<endl;;
       }
    }

};

Comments

Popular posts from this blog

C++ Guidelines for Multithreaded System

Signalling System #7(SS7) Protocol.

std::shared_ptr