C++11 bad weak pointer : class thrown as exception by shared_ptr

The C++11 bad weak pointer(or bad_weak_ptr) a class thrown as an exception by the shared_ptr, why is it call bad_weak_ptr? what is its purpose? these are some of the questions we will explore in this post.

Link :C++11 shared_ptr

The C++11 weak_ptr has lesser influence over the memory of shared_ptr hence it is known as the weak pointer. Directly accessing the shared_ptr storage with weak_ptr is not possible, however, it can share the resource it manages with other shared_ptr.

Link :C++11 weak_ptr

When weak_ptr is used as an initializer of shared_ptr the reference count of the memory will increase. Hence, sharing of resources through weak_ptr is secure as it follows the usual norm of reference counting; increase the reference count whenever new object points to the existing storage.

shared_ptr<int>sp(new int(98)) ;

weak_ptr<int>wp(sp) ;

{
shared_ptr<int>sp1(wp) ;
cout<< sp1.use_count ;
}

If you run the code above and if all is good you get the output as 2; the reference count. Note here I said “if all is good” that means some things can go wrong. And one such possible case of going-wrong is if the weak_ptr ‘wp’ does not point to the valid storage.

If the weak_ptr wp does not point to any storage then the shared_ptr will throw an exception.The exception thrown is of bad_weak_ptr class type. Simply put, shared_ptr will throw bad_weak_ptr as an exception if the weak_ptr which is pass as an argument does not point to any valid storage.

shared_ptr<int>sp(new int(98)) ;

weak_ptr<int>wp(sp) ;

wp.reset() ;

{
shared_ptr<int>sp1(wp) ; //throws an exception
}

To get a better view of the exception thrown let’s include try() and catch() statement in our code.

shared_ptr<int>sp(new int(98) ) ;
weak_ptr<int>wp(sp) ;

wp.reset();

try
{
 shared_ptr<int>sp1(wp) ; //throws an exception
}
catch( bad_weak_ptr )
{
 cerr<< “exception thrown” ;
}

Note bad_weak_ptr is thrown only by shared_ptr and only when the argument is weak_ptr type. So obviously a weak_ptr accepting a weak_ptr as initializer which does not point to any storage will not throw bad_weak_ptr as an exception.

shared_ptr<char>spCh(new char(’56’) ) ;
weak_ptr<int>wp ,

wp1(wp); //does not throw an exception


Defining the bad_weak_ptr class

bad_weak_ptr is a class derived from exception class and since it is a class it has two member functions.

i)virtual destructor and
 
ii)virtual what() function.

It is important to make these two functions virtual, why? It is discussed in Chapter 9. We can define the bad_weak_ptr class as shown below.

class bad_weak_ptr : public std::exception
{
public:
 virtual char const* what() const noexcept;
 virtual ~bad_weak_ptr() noexcept { }
};

//definition of what( ) function
const char* bad_weak::what( ) const noexcept
{ return “bad_weak_ptr” ; }

We can re-write the code shown earlier where wp: a weak_ptr which does not point to any valid storage, is used as an initializer of shared_ptr sp1 to make our client more aware of the reason as to why an exception is thrown.

shared_ptr<int>sp(new int(98) ) ;
weak_ptr<int>wp(sp) ;

wp.reset();

try
{
 shared_ptr<int>sp1(wp) ; ///throws an exception
}
catch( bad_weak_ptr &bwPtr )
{
 cerr<< bwPtr.what();
}

Now the code outputs “bad_weak_ptr”, looking at this error message I am sure the client can figure out what measures to take against the exception thrown.


Implementing bad_weak_ptr in our class

There is no hard and fast rule in C++ which state that bad_weak_ptr should only be thrown by shared_ptr class type. We can make any of our class throw bad_weak_ptr as an exception. By doing this we are taking full advantage of the language asset and also making sure that an exception of specific kind remains uniform in our programs. This,in turn can, make debugging our program easier. To put it bluntly, if shared_ptr or any of our class throws bad_weak_ptr we can easily make out that the problem lies in the weak_ptr pass as an argument and not go look somewhere else.

Suppose, we have a class whose one of its data members is a shared_ptr type. To make this class robust we will make one of its constructors accept a weak_ptr type. This constructor will throw bad_weak_ptr as an exception if the weak_ptr does not point to any valid storage.

template<class T>
class A
{
 shared_ptr<T> aSp ;

public:
 A(shared_ptr<T> ptr):aSp( ptr ) { }

 //constructor accepting weak_ptr
 A(const weak_ptr<T> &wp )
 {
 if( wp.expired() )
  {
  throw bad_weak_ptr( ) ;
  }
  else
  aSp=wp.lock( ) ;
 }

int get_value( ) const { return *aSp; }

~A( ) { }
};

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

cout<< a.get_value() << endl ;

weak_ptr<int>wp ;

try{
A<int>aa(wp) ;
}
catch(bad_weak_ptr &bw )
{
cerr<< bw.what() ;
}

cin.get();
return 0;
}

If weak_ptr is empty, exception is thrown, but if it is not lock( ) function is called. Since lock() function increase the reference count there is no room for any unexpected output here.