C++11 weak pointer : a weaker shared_ptr

Here we will discuss another smart pointer known as the C++11 weak pointer(or weak_ptr).We will see some of the points on why weak_ptr is necessary and why it is called a weaker shred pointer.

The smart pointer: shared_ptr, is more dynamic and secure than the traditional raw pointer but it seems to suffer from some drawbacks which cannot be resolved by any using shared_ptr itself(please visit this link to read about the drawbacks of shared_ptr 4 disadvantage of C++ shared pointer).

The limitations faced by the shared_ptr is the main culprit behind the introduction of weak_ptr in C++11. Although using a shared_ptr may not be as dangerous as we might imagine but not providing a solution for the drawbacks will leave a big hole in the features of shared_ptr which one might consider intimidating. One can say the existence of weak_ptr makes shared_ptr more secure by keeping the shared_ptr at bay and not using it directly.

In this post we will see some ways to implement weak_ptr and also see how weak_ptr can solve the cyclic reference problem.

weak_ptr

The most important thing to note about weak_ptr is it cannot own a resource of its own. This means like other smart pointers we cannot allocate a new memory and make the weak_ptr manage that memory. It can only point to the resource of another smart pointer: the shared_ptr.

To sum it up weak_ptr can only point to the resource of shared_ptr. If there is no shared_ptr resource weak_ptr has no purpose, it will be like a ronin with no master to serve; doodling purposelessly. And since it does not operate the resource directly the term ‘weak’ is coined for the smart pointer.

To make a weak_ptr point to the shared_ptr resource the shared_ptr should be passed as an argument during weak_ptr declaration or you can use the ‘=’ operator to assign the shared_ptr to weak_ptr. Initializing weak_ptr
using both of these methods are shown below.

shared_ptr<string>sp(new string(“I like Xbox”) );

weak_ptr<string>wp(sp) , //works fine
  wp1 ;

wp1=sp ; //works fine


Accessing the value pointed by the weak_ptr

To access the value the weak_ptr manages we cannot use the ‘*’ operator like the other smart pointers.weak_ptr cannot access the value directly. To access the value we have to create another instance of the shared_ptr that point to the memory managed by the weak_ptr and using this shared_ptr we can access the value.

To create a shared_ptr from a weak_ptr the member function lock() is use. This function returns a shared pointer of the object which the weak_ptr manages.Consider the program below.

shared_ptr<string>sp(new string(“hard drvie”) ) ;

weak_ptr<string>wp(sp) ;

auto spNew=wp.lock( ) ;

cout<< *spNew ;

Using the lock() function can get nasty if not careful.Suppose the resource which the weak_ptr points to is already destroyed then the lock() function will be returning a shared_ptr that points to some invalid storage and accessing it will give an undefined result.

Whenever lock() is used we must make sure to validate the weak_ptr first. This can be done by using the normal if() statement,consider the code below.

weak_ptr<int>wp(sp) ;
{
shared_ptr<int>sp(new int(890) );
wp=sp ;
}

auto spNew=wp.lock( ) ; //not recommended
cout<< *spNew ; //undefined

if( auto spValid=wp.lock() ) //check for validity,recommended
{
  cout<< *spValid ;
}
else
  cout<< “Storage has expired” ;

If the storage is valid the value of spValid is printed out else “Storage has expired” is printed out.


Cyclic reference problem solved by weak_ptr

In another post I have pointes out 4 disadvantage or limitations of shared_ptr. In this section we will see how to solve one of the limitations of shared_ptr namely the cyclic reference problem using weak_ptr.

The cyclic reference is a case when the shared_ptr will never achieved it’s reference count as 0 and so the memory gets leak in the program.An instance of cyclic reference case is shown below.

class A
{
public:
 shared_ptr<B>spB ;
 A() { }

 ~A( ) { cout<<“A destructor \n”; }
};

class B
{
public:
 shared_ptr<A>spA ;

 B() { }
 ~B( ) { cout<< “B destructor \n”; }
};

int main( )
{
shared_ptr<A>a(new A() ) ;
shared_ptr<B>b(new B() ) ;

a->spB=b ; //spB points to b
b->spA= a ; //spA points to a

return 0 ;
}

The reference count of the shared_ptr a and b will never reduce to 0 so the destructor is never called leading to memory leakage.But if we use a weak_ptr instead of shared_ptr as the data member inside both the classes we can easily diverge from the cylic reference issue.

class A
{
public:
 weak_ptr<B>spB ;
 A() { }
 ~A( ) { cout<<“A destructor \n”; }
};

class B
{
public:
 weak_ptr<A>spA ;

 B() { }
 ~B( ) { cout<< “B destructor \n”; }
};

int main( )
{
shared_ptr<A>a(new A() ) ;
shared_ptr<B>b(new B() ) ;

a->spB=b ; //spB points to b
b->spA= a ; //spA points to a

return 0 ;
}

Using weak_ptr does not increase the reference count of the shared_ptr a and b. So the reference count of ‘a’ and ‘b’ is 1, but if shared_ptr is used the reference count will increment to 2.

In case of weak_ptr when the object ‘a’ goes out of scope the reference count is decreased to 0 and so the object’s destructor is called and frees the storage, ditto happens for the object ‘b’.But if shared_ptr is used, when object ‘a’ goes out of scope the reference count will reduce to 1 and so the destructor cannot be called leading to memory leakage, the same goes for the object ‘b’.

The member functions of weak_ptr is given here weak pointer Member functions.