Member functions of shared_ptr class: use_count() – unique() – get() – swap( ) – reset( )


The shared pointer class template like any other class has some member functions defined for use by it’s object.Some of these functions may be inevitable in our program and the others may not but knowing them might give you an edge over the other.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.After all what we are really interested in is the object of the share_ptr class:due to it’s smarter behavior and reliability.
 
*Note:if you don’t know what is shared_ptr do not proceed you can use this link to read about them.
 
We will discussed 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 use_count() function returns the number of reference value,the unique() function also behave more or less like the use_count() but it perform 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 for 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 the value for st.unique( ) 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 point 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).By using the decltype() we are deducing only the type of st and making sp as an object of that type.There should be no ambiguity here that since sp type is determined from st using decltype(),sp should also point to st storage,this is just wrong.So in actual sp hasn’t point to any dynamic storage and so it’s reference count is 0 and aso 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 allow the address to be assigned to raw pointer? don’t be so surprise because I understand the situation we are in.We can already foresee many dangerous situation our program may run into because that’s what happen 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 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 from generating a 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 solution:using 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.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 of how the storage is made depend on the type for which the object is created.So if the type are 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 :function that return reference value, function that return 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 it’s 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 argument affect the behavior 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 occur when no argument is passed i.e the storage get destroyed.But here the function perform one more activity, the ownership of the storage of the argument passed is also now assigned to the shared_ptr object.This implies that the object has gain accessed to another storage.An example program below will prove it.
 

shared_ptr<int&gt 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 possibility that iNew will be left out as 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 a dynamic memory storage and the second is a function name.Such function is also know as deleter function.For the second argument -which is a deleter function name- to be valid a 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 the type which shared_ptr class is declared for 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 the 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.
 


Related Link

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