shared pointer member functions : use_count , unique, get – swap, reset

The C++11 shared_tr class template has some member functions use_count() – unique() – get() – swap( ) – reset( ). Some of these functions may be inevitable in our program and the others may not but knowing them might be useful.The task of all the member functions seem to be rather simple and they are easy to implement-you don’t have to sweat over them- and so it’s not hard understanding them.

*Note:if you don’t know what is shared_ptr do not proceed you can use this link to read about them.

We will discuss five of it’s member function here:

i)use_count
ii)unique()
iii)get()
iv)swap()
v)reset


use_count()

Shared_ptr class uses a method known as reference counting to keep track of how many other shared_ptr objects are pointing to the same storage.The element that holds this value: number of pointer/object pointing to the same storage,is known as reference value.The function use_count() does nothing but return this reference value of a particular shared_ptr object.

void func(shared_ptr<int>i ) //i points to storage same as sp1
{
cout<< i.use_count() << endl ;
}

int main( )
{
shared_ptr<int> sp1(new int(8)) ;
cout<< sp1.use_count( ) << endl ;

func( sp1 ) ;

cout<< sp1.use_count( ) << endl ;
 {
 shared_ptr<int>sp2(sp1) ; //sp2 points to  storage same as sp1
 shared_ptr<int>sp3(sp2) ; //sp3 points to storage same as sp1

 cout<< sp3.use_count() << endl ;
 } //sp2 and sp3 are destroyed here

cout<< sp1.use_count( ) ;

cin.get( ) ;
return 0 ;

}

The output of this program is:

1
2
1
3
1

I won’t be explaining the reason behind this output, try to justify it yourself, it’s your homework!



unique()

The shared_ptr use_count() function returns the number of reference value, the unique() function also behave more or less like the use_count() but it performs one extra activity. This function will check if the reference count is 1 or more, if the reference count is 1 it returns true but if it is more than one it returns false.So the return type of unique() function is bool.No doubt the unique() function does live up to it’s name;it checks if a shared_ptr object is unique or not.

int main( )
{
shared_ptr<string>st(new int(2) ) , str ;
shared_ptr<double>pi(new double(3.1415)) ;

auto =s(st) ; //Check C++11 for auto function

cout<< st.unique( ) << endl ;
cout<< str.unique( ) <<endl ;
cout<< pi.unique( ) << endl ;

decltype(st) sp ;

cout<< unique(sp) << endl ;

cin.get( ) ;
return 0 ;
}

The output is:

0
0
0
1
0
0

If we examine the output of st.unique( ) which is 0(false),this is expected as ‘s’ also points to the storage st points to and vice versa.

The output for str.unique() is also 0(false), this is little surprising but it is correct.The str pointer hasn’t pointed to any dynamic storage yet so it’s reference count is 0.Hence the false value.

pi on the other hand point to only one known storage so it’s reference count is 1. Hence, pi.unique() renders true.

In case of sp it’s type is deduced using the decltype(st) (if you don’t know
what is decltype( ) visit this link C++11 decltype()).By using the decltype() we deduce the type of st and make sp as an object of that type.There should be no ambiguity hereregarding the fact that since ‘sp’ type is determined from ‘st’ using decltype(), sp should also point to st storage, this is just wrong. So ‘sp’ hasn’t pointed to any dynamic storage and so it’s reference count is 0 and also sp.unique() will give return false.


get( )

For me this is the most interesting function among all the shared_ptr member functions.This function returns the address of the storage the shared_ptr object is responsible for.The address returned is just plain hexadecimal value so it can be assigned to raw pointer, What! it allows the address to be assigned to a raw pointer? don’t be so surprised because I understand the situation we are in.

We can already foresee many dangerous situations our program may run into because that’s what happens when we mingle raw pointer and shared_ptr in the ownership of a dynamic storage.Consider the program below.

int *i=nullptr ;

 {
 shared_ptr<string> spI(new string() ) ;
 i=spI.get( ) ;
 }

//i is left as a dangling pointer

The ‘i’ pointer is left as dangling pointer not pointing to any valid memory. There is another case little different from the one shown above, here the raw pointer pointing to the shared_ptr object memory is assigned to another shared_pointer object. The result is the raw pointer and the shared_ptr object are left out as danglers.

void func( int *i )
{
shared_ptr<int>spA=static_cast< shared_ptr<int> >(i) ;
cout<< *spA << endl ;

}//storage is freed here

int main( )
{
shared_ptr<int >si( new int(98) ) ;

int *i=si.get() ;

func(i) ;

int value=*i ;   //undefine 
int val=*si ;  //undefine

return 0 ;
}

It seems wherever we go and use the get() function only trouble awaits us. But it doesn’t mean we cannot take any measure to counteract against such error.

The simplest solution to prevent generating any dangling pointer is to assign a nullptr to it after the shared_ptr object goes out of scope. However, there is no sure way to check if a shared_ptr object is still valid or not in any region of the program. So this method cannot be applied effectively in all the cases.This means it all comes down to one simple rule: use get() function sparingly and judiciously.



swap( )

There may be certain situation in your program where you want two shared_ptr objects to swap their storage.In such scenario the swap() function might come in handy.This function does nothing, it takes two shared_ptr object and swap their pointers or address.After this function is called the storage gets swap and also the reference count associated with each shared_ptr object is exchanged.

shared_ptr<char> spC(new char(‘G’)) , spC1(spc) ;

shared_ptr<char> spCh(new char(‘O’)) ;

swap(spC , spCh ) ;
// spC.swap( spCh ) ; or spCh.swap(spC) ; //Also work fine

cout<< *spC << ” ” << spC.use_count() << endl
    << *spCh << ” ” << spCh.use_count() << endl ;

Run the program and examine the output it might be worth it.

A thing to note while using swap() is you must make sure that the two pointers used are of the same type.If they are of different type the compiler will complain.

shared_ptr<int> is(new int(9)) ;
shared_ptr<string> ss(new string(“Oh God!”));

swap(is,ss) ; //error!

By swapping two pointers we are exchanging the storage which they point to. The information on how the storage is made depends on the type of object created. So if the type is different and swapping is allowed the value that we might get when accessing the storage will be very unexpected. Also, note C++ is very strict about type so the compiler just won’t allow it.


reset()

We have seen different type of member functions of shared_ptr class: a function that returns reference value,
a function that returns an address of the storage and a function that swap two pointers. Well, we need one more function: a function that can clean up the storage even before the object runs out of scope.

This function is known as reset() function. This function can accept zero, one or two arguments and depending on the number of arguments passed its can function vary.

In the case where passing an argument is required the argument must be a pointer to dynamic storage and their type must be same: the type of argument and the type for which shared_ptr class is declared. We will examine each case and see how passing different number of arguments affect the behaviour of the function.

reset( ) function call with zero argument

When no argument is passed to reset() function it simply reset the pointer. It means the storage will get destroyed and so the pointer will not point to any valid memory.

shared_ptr<double>spD(new double(9.87) );

spD.reset( ) ;

cout<< *spD << endl ; //undefine
reset() function call with one argument.

If one argument is passed the same process occurs as when no argument is passed i.e. the storage get destroyed. But here the function performs one more activity, the ownership of the storage of the argument passed is also now assigned to the shared_ptr. This implies that the shared_ptr has gained access to another storage. An example program below will prove it.

shared_ptr<int> i(new int(90)) ;

int *iNew=new int(78) ;

i.reset(iNew) ; //i now assume ownership of new storage

i.reset(new int(78)) ; //this also work fine

The first reset() call is insecure because there is a possibility that iNew will be left out as a dangling pointer, so you should opt the second reset() call.

reset( ) function call with two arguments.

When a reset() function accept two arguments the first is obviously the pointer to the dynamic memory storage and the second is a function name. Such function(pass as the second argument) is also known as deleter function.

For the second argument -which is a deleter function name- to be valid the function with that name must exist. The purpose of this function is to delete the storage which the shared_ptr pointer points to. This means the function will accept a pointer of shared_ptr object type and this function is called only when the shared_ptr object goes out of scope. The call is automatic and handle by the compiler. An example program is shown below.

void func(string *s)
{
delete s ;
}

int main( )
{
 {
 shared_ptr<string> spS(new string(“God”)) ;

 spS.reset(new string(“is great”), func ) ;

 cout<< *spS <<endl ;
 } //func() is called and storage is deleted

cout<< *spS ; //undefined

cin.get( ) ;
return 0;
}

If the second argument func() is not provided ‘spS’ will call it’s delete operator function to delete the storage.So the second argument function is just an alternative function that is called to delete the storage.