C++11 bad_weak_ptr : a class thrown as exception by shared_ptr


C++11 bad_weak_ptr

weak_ptr due to it’s lesser influence over the memory of shared_ptr it manage it is known as weak pointer.Directly accessing the storage with weak_ptr is not possible however,it can share the resources it manages with other shared_ptr.When weak_ptr is used as the initializer for 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 reference count when new object points to the same 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 valid storage.So what happens if wp does not point to any 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(which is a class) as an exception if the weak_ptr used 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” ; }

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

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 also make 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 the same in all our program.This in turn can make debugging of the 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 argument and not go look somewhere else.

Suppose we have a class whose one of it’s data members is shared_ptr type.To make this class robust we will make one of it’s constructor 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.

Related Link

->Smart pointer : Shared pointer ; why is it call shared pointers?