Click here to Skip to main content
Click here to Skip to main content

C++11 Smart Pointers

By , 11 Feb 2013
Rate this:
Please Sign up or sign in to vote.
This is an old version of the currently published article.

Introduction

Ooops. Yet another article on smart pointers of C++11. Now a days I hear lot of people talking about the new standard of C+11 which is nothing but C++0x/C++11. I went through some of the language features of C++11 and its really an amazing work. I'll focus only on the smart pointers section of the C++11.

Background

What are the issues with normal/raw/naked pointer?

Lets go one by one.

People refrain to use pointers as it gives lot of issues, if not handled properly. Thatswhy the newbie programmers dislike the pointers. Many issues are involved with the pointers like ensuirng the lifetime of the objects referred by pointers,dangling references and memory leaks.

Dangling reference is caused if a memory blocked is pointed by more than one pointer variable and if one of the pointer is released.

As all of you know, memory leaks occus when a block memory is fetched from the heap and is not released back.

People say I write clean and error proof code. Why should I use the smart pointer? And a programmer asked me, "Hei this is the following code. I fetched the memory from heap, manipulated it and once after that I released it properly. What is the need of smart pointer? "

void Foo( )
{
    int* iPtr = new int[5];
 
    //manipulate the memory block
    .
    .
    .
 
    delete[ ] iPtr;    
}

The above code works fine and memory is released properly under ideal circumstances. But think of the practical environment of code execution. The instructions between the memory allocation and releasing, can do nasty thing like accessing a invalid memory location, dividing by zero or say another programmer pitch into your program to fix a bug and adds a permature return statement based on some condition.

In all the above cases, you will never reach the point where the memory is released. Because the first two cases throws an exception where as the third one is a prematured return.So the memory is getting leaked while program is running.

The one stop solution for all of the above issues is Smart Pointers [ if they are really smart enough ].

What is a smart pointer?

Smart pointer is a RAII modeled class to manage the dynamically allocated memory. It provides all the interfaces provided by the normal pointers with few exceptions. During construction it owns the memory and release the same when it goes out of scope, In this way, the programmer is free about managing the dynamically allocated memory.

C++98 has introduced first of its kind called auto_ptr.

auto_ptr:

Lets see the use of auto_ptr and how smart it is to resolve the above issues?

class Test
{
public:
 Test(int a = 0 ) : m_a(a)
 {
 }
 ~Test( )
 {
  cout<<"Calling destructor"<<endl;
 }
public:
 int m_a;
};
 
void main( )
{
 std::auto_ptr<Test> p( new Test(5) ); 
 cout<<p->m_a<<endl;
} 
 

The above code is smart to release the memory associated with it. What we did is, we fetched a memory block to hold an object of type Test and associated it with the auto_ptr p. So when p goes out of scope, the associated memory block is also released.

//***************************************************************
class Test
{
public:
 Test(int a = 0 ) : m_a(a)
 {
 }
 ~Test( )
 {
  cout<<"Calling destructor"<<endl;
 }
public:
 int m_a;
};
//***************************************************************
void Fun( )
{
 int a = 0, b= 5, c;
 if( a ==0 )
 {
  throw "Invalid divisor";
 }
 c = b/a;
 return;
}
//***************************************************************
void main( )
{
 try
 {
  std::auto_ptr<Test> p( new Test(5) ); 
  Fun( );
  cout<<p->m_a<<endl;
 }
 catch(...)
 {
  cout<<"Something has gone wrong"<<endl;
 }
}

In the above case, the exception is thrown but still the pointer is released properly. This is because of the stack unwinding which happens when an exception is thrown. As all the local objects belonging to the try block is destryoed, p goes out of scope and it releases the associated memory.

Issue1: So far auto_ptr is smart. But it has more fundamental flaws over its smartness. Auto_ptr transfer the ownership when it is assigned to another auto_ptr. This is really an issue while passing the auto_ptr between the functions. Say, I have a auto_ptr in Foo( ) and this pointer is passed another function say Fun( ) from Foo. Now once Fun( ) complete its execution, the ownership is not returned back to Foo.

//***************************************************************
class Test
{
public:
 Test(int a = 0 ) : m_a(a)
 {
 }
 ~Test( )
 {
  cout<<"Calling destructor"<<endl;
 }
public:
 int m_a;
};
 
 
//***************************************************************
void Fun(auto_ptr<Test> p1 )
{
 cout<<p1->m_a<<endl;
}
//***************************************************************
void main( )
{
 std::auto_ptr<Test> p( new Test(5) ); 
 Fun(p);
 cout<<p->m_a<<endl;
} 

The above code cause program crash because of the weired behavior of auto_ptr. What happens is that, p owns a memory block and when Fun is called, p transfers the ownership of its associated memory block the auto_ptr p1 which is the copy of p. Now p1 owns the memory block which was previously owned by p. So far its fine. Now fun has completed its execution, and p1 goes out of scope and the memory blocked is released. How about p? p does not own anything, thatswhy it cause crash when the next line is executed which access the p thinking that it owns some resource.

Issue2: Yet another flaw. auto_ptr cannot be used with array of objects. I mean it cannot be used with the operator new[ ].

//***************************************************************
void main( )
{
 std::auto_ptr<Test> p(new Test[5]);
} 
 

The above code gives runtime error. This is because when the auto_ptr goes out of scope, delete is called on the associated memory block. This is fine, if the auto_ptr owns only a single object. But in the above code, we have created array of objects on the heap which should be destroyed using delete[ ] and not delete.

Issue3: auto_ptr cannot be used with the standard containers like vector,list, map etc.,

As the auto_ptr is more error prone and it will be deprecated and the C++ 11 has come with a new set of smart pointers, each has its own purpose.

  • shared_ptr
  • unique_ptr
  • weak_ptr

shared_ptr:

Ok get ready to enjoy the real smartness. The first of its kind is the shared_ptr which has the notion called shared ownership. The goal of shared_ptr is very simple: Multiple shared pointers can refer a single object and when the last shared pointer goes out of scope, the memory is being relased automatically.

Creation:

void main( )
{
 shared_ptr<int> sptr1( new int );
}

Make use of the make_shared macro which expedites the creation process. As the shared_ptr allocate memory internally, to hold the reference count, make_shared( ) is implemented in way to do this job effectively.

void main( )
{
 shared_ptr<int> sptr1 = make_shared<int>(100);
}

The above code creates shared_ptr which points to a memory block to hold an integer with value 100 and reference count 1. If another shared pointer is created out of sptr1, the reference count goes upto 2. This count is know as strong reference. Apart from this, the shared pointer has another reference count known as weak reference, which will explained while visiting weak pointer.

You can find out the number of shared_ptrs referring to the resource by just getting the reference count by calling the use_count( ). And while debugging, you can get it by watching the stong_ref of the shared_ptr.

Destruction:

shared_ptr releases the associated resource by calling the delete by default. If the user needs a different destruction policy, he/she is free to specify the same while constructing the shared_ptr. The following code is a source of trouble due to the default destruction policy.

class Test
{
public:
 Test(int a = 0 ) : m_a(a)
 {
 }
 ~Test( )
 {
  cout<<"Calling destructor"<<endl;
 }
public:
         int m_a;
};
void main( )
{
 shared_ptr<Test> sptr1( new Test[5] );
}
 
 
 

Because the shared_ptr owns an array of objects, it calls delete when goes out of scope. Actually, delete[ ] should have been called to destroy the array. User can specify the custom deallocator by a callable object ie., a function, lamda experssion, function object.

void main( )
{
 shared_ptr<Test> sptr1( new Test[5], 
        [ ](Test* p) { delete[ ] p; } );
}

The above code works fine as we have specified the destruction should happen via delete[ ].

Interface:

shared_ptr provides dereferencing operators *, -> like the normal pointer provides. Apart from that it provides some more important interface like

1. get( ) : To get the resource associated with the shared_ptr.

2. release( ) : To yield the ownership of the associated memory block. If this is the last shared_ptr owning the resource, then the resource is released automatically.

3. unique: To know whether the resource is managed by only this shared_ptr instance.

4. operator bool: To check whether the shared_ptr owns a memory block or not. Can be used with the if condition.

OK thatsall about the shared_ptr. But shared_ptr too have few issues.

Issues:

1. If a memory is block is associated with the shared_ptrs belonging to a different group, then there is an error. All the shared_ptrs sharing the same reference count belong to a group. Lets see an example.

void main( )
{
 shared_ptr<int> sptr1( new int );
 shared_ptr<int> sptr2 = sptr1;
 shared_ptr<int> sptr3;
 sptr3 = sptr2;
}
 

The below table gives you the reference count values for the above code.

All the shared_ptrs shares the same reference count hence belonging to same group. The above code is fine. Let's see another piece of code.

void main( )
{
 int* p = new int;
 shared_ptr<int> sptr1( p);
 shared_ptr<int> sptr2( p );
}

The above piece of code is going cause an error because 2 shared_ptrs from different group share a single resource. The below table gives you picture of the root cause.

To avoid this, better dont to create the shared pointers from the naked pointer.

2. There is another issue involved with creating shared pointer from the naked pointer. In the above code, consider that only one shared pointer is created using p and the code works fine. Consider, by mistake if programmer deletes the naked poiner p, before the scope of shared pointer ends. Oooppss!!! Yet another crash..

3. Cyclie Reference: Resources are not released properly, if cyclic reference of shared pointers are involved. Consider the following piece of code.

class B;
class A
{
public:
 A(  ) : m_sptrB(nullptr) { };
 ~A( )
 {
  cout<<" A is destroyed"<<endl;
 }
 shared_ptr<B> m_sptrB;
};
class B
{
public:
 B(  ) : m_sptrA(nullptr) { };
 ~B( )
 {
  cout<<" B is destroyed"<<endl;
 }
 shared_ptr<A> m_sptrA;
};
//***********************************************************
void main( )
{
 shared_ptr<B> sptrB( new B );
 shared_ptr<A> sptrA( new A );
 sptrB->m_sptrA = sptrA;
 sptrA->m_sptrB = sptrB;
}

The above code has cyclic reference. I mean the class A holds shared pointer to B and class B holds shared pointer to A. In this case, the resource associated with both sptrA and sptrB are not released. Refer the below table.



Reference count of both sptrA and sptrB goes down to 1, when they go out of scope and hence the resources are not released!!!!!

To resolve the cyclic reference, C++ provides another smart pointer class called weak_ptr.

Weak_Ptr:

Weak pointer provides the sharing semantics and not the owning semantics. Means weak pointer can share a resource held by the shared_ptr. So to create weak pointer, already some body should own the resource which is nothing but a shared pointer.

Weak pointer does not allow the normal interfaces supported by a pointer like calling *, ->. Because it is not the owner of resource and hence it does not give any chance the programmer to mishandle it. Then how to make use of the weak pointer.

The answer is to create a shared_ptr out of the weak _ptr and use it. Because this make sures that the resource wont be destroyed while using by incrementing strong reference count. As reference count is incremented, its very sure that the count will be atleast 1 till you complete using the shared_ptr created out of the weak_ptr. Otherwise, what may happen is while using the weak_ptr, the resource held by the shared_ptr go out of scope and the memory is released which creates chaos.

Creation:

Weak pointer constructor takes shared pointer as one of its parameter. Creating a weak pointer out of the shared pointer, increases the weak reference counter of the shared pointer. This means that the shared pointer shares it resource with another pointer. But this counter is not considered to release the resource when the shared pointer goes out of scope. I mean if the storng reference of the shared pointer goes to 0, then the resource is released irrespective of the weak reference value.

void main( )
{
 shared_ptr<Test> sptr( new Test );
 weak_ptr<Test> wptr( sptr );
 weak_ptr<Test> wptr1 = wptr;
}

We can watch the reference counters of the shared/weak pointer.

Assigning one weak pointer to another wak pointer, increases the weak reference count.

So what happens, when a weak pointer points to a resource held by the shared pointer and the shared pointer destroys the associated resource when it goes out of scope? The weak pointer get expired.

How to check whether the weak pointer is pointing a valid resource? There are 2 ways.

1. Call the use_count( )method to know the count.Note that, this method returns the strong reference count and not the weak reference.

2. Call expired( ) method. This is faster than calling use_count( ).

To get a shared_ptr from a weak_ptr call lock( ) or directly by casting the weak_ptr to shared_ptr.

void main( )
{
 shared_ptr<Test> sptr( new Test );
 weak_ptr<Test> wptr( sptr );
 shared_ptr<Test> sptr2 = wptr.lock( );
}

Getting the shared_ptr from the weak_ptr increases the strong reference as said earlier.

Now let's see how the cyclic reference issue is resolved using the weak_ptr.

class B;
class A
{
public:
 A(  ) : m_a(5)  { };
 ~A( )
 {
  cout<<" A is destroyed"<<endl;
 }
 void PrintSpB( );
 weak_ptr<B> m_sptrB;
 int m_a;
};
class B
{
public:
 B(  ) : m_b(10) { };
 ~B( )
 {
  cout<<" B is destroyed"<<endl;
 }
 weak_ptr<A> m_sptrA;
 int m_b;
};<pre>void A::PrintSpB( )
{
 if( !m_sptrB.expired() )
 {  
  cout<< m_sptrB.lock( )->m_b<<endl;
 }
}<pre>void main( )
{
 shared_ptr<B> sptrB( new B );
 shared_ptr<A> sptrA( new A );
 sptrB->m_sptrA = sptrA;
 sptrA->m_sptrB = sptrB;
 sptrA->PrintSpB( ); 
}

Unique_ptr:

This is almost a kind of replacement to the error prone auto_ptr. Unique_ptr follows the exclusive ownership semantics ie., at any point of time, the resource is owned by only one unique_ptr. When the auto_ptr goes out of scope, the resource is released. If the resource is overwritten by some other resource, the previously owned resource is released. So it gurantees that the associated resource is released always.
Creation:

The unique_ptr is created in a same way as shared_ptr except it has an additional facility for array of objects.

unique_ptr<int> uptr( new int );
unique_ptr class provides the specialization, to create array of objects, which calls delete[ ] instead of delete, when the pointer goes out of scope. The array of objects can be specified as a part of template parameter while creating the unique_ptr. In this way, the programmer dont have to provide a custom deallocator, as unique_ptr does it.
unique_ptr<int[ ]> uptr( new int[5] );
Ownership of the resource can be transferred from one unique_ptr to another by assigning it.
Keep in mind that, unique_ptr does not provide you copy semantics [ copy assignment and copy consturction is not possible ] but move semantics.
unique_ptr<int> uptr( new int );
unique_ptr<int> upt2 = uptr; //COMPILE ERROR
unique_ptr<int> upt3 = std::move( uptr )
unique_ptr<int> uptr4( uptr ); //COMPILE ERROR
unique_ptr<int> uptr5( std::move(uptr) );
)
In the above case, if upt3 and uptr5 owns some resource already, then it will be destroyed properly before owning a new resource.
Interface:
The interface what unique_ptr provides is very similar to the ordinary pointer but no pointer arithmetic is allowed.
unique_ptr provides a function called release which yields the ownership. Difference between release( ) and reset( ), release just yields the ownership and not destroys the resource where as reset destroys the resource.

Which one to use?

It purely depends upon how do you want to own a resource? If shared owneship is needed then go for shared_ptr otherwise unique_ptr.
Apart from that, shared_ptr is bit heavier than the unique_ptr, because internally it allocates memory to do lot of book keeping like strong reference, weak reference etc. But unique_ptr does not need these counters as it is the only owner for the resource.

Using the code

Have attached the code to explain the details of the each pointer. Have added enough comments to each instruction. Ping me back if you find any problem with the code. The weak pointer example demonstrates what is the problem with shared pointers in case of cyclic reference and how the weak pointer resolves it.
   

History

This is the first version of the article. I'll keep updated based on the feedback and comments.


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

syed_babu
Software Developer
India India
I'm working as Senior software Engineer since 7 years and interested in MFC and COM programming.

Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
QuestionGreat article PinprofessionalAmbarRay11-Nov-13 1:12 
AnswerRe: Great article Pinmembersyed_babu11-Nov-13 18:26 
QuestionMy Vote of 5 PinmemberAjay_Tiwari27-Sep-13 9:04 
AnswerRe: My Vote of 5 Pinmembersyed_babu29-Sep-13 17:29 
GeneralMy vote of 5 PinmemberAjay_Tiwari27-Sep-13 9:03 
GeneralGreat Article [modified] PinprofessionalGhanshyam Mann17-Aug-13 10:31 
GeneralRe: Great Article Pinmembersyed_babu18-Aug-13 19:46 
GeneralMy vote of 5 Pinmembermaniappa7-Jul-13 5:09 
GeneralRe: My vote of 5 Pinmembersyed_babu18-Aug-13 19:47 
QuestionThread safety PinmemberStorm-blade2-Jul-13 22:35 
AnswerRe: Thread safety Pinmembersyed_babu4-Jul-13 17:32 
SuggestionTypo in nice article. Pinmemberdavyzhu26-Jun-13 20:47 
GeneralRe: Typo in nice article. Pinmembersyed_babu2-Jul-13 18:18 
GeneralMy vote of 5 PinmemberMihai MOGA11-Mar-13 21:49 
GeneralRe: My vote of 5 Pinmembersyed_babu13-Mar-13 17:47 
GeneralMy vote of 5 PinmemberJohn Bandela14-Feb-13 14:35 
GeneralRe: My vote of 5 Pinmembersyed_babu14-Feb-13 17:41 
GeneralNice introduction to std::shared_ptr and friends PinmvpEspen Harlinn12-Feb-13 4:57 
GeneralMy vote of 1 PinmemberJiří Miklík12-Feb-13 3:03 
GeneralRe: My vote of 1 Pinmembersyed_babu12-Feb-13 3:19 
GeneralRe: My vote of 1 Pinmembersyed_babu12-Feb-13 3:23 
GeneralRe: My vote of 1 PinmemberSentenryu18-Feb-13 3:57 
GeneralRe: My vote of 1 Pinmember_Manuel1-Sep-13 10:57 
GeneralRe: My vote of 1 Pinmembersyed_babu1-Sep-13 17:20 
GeneralRe: My vote of 1 PinmemberJohn Bandela27-Sep-13 4:11 
QuestionOne small step for a man..... PinmemberJiří Miklík12-Feb-13 2:57 
AnswerRe: One small step for a man..... Pinmembersyed_babu12-Feb-13 3:18 
AnswerRe: One small step for a man..... PinmemberDavid Serrano Martínez12-Feb-13 3:39 
GeneralRe: One small step for a man..... PinmvpEspen Harlinn12-Feb-13 5:02 
GeneralRe: One small step for a man..... Pinmembersyed_babu12-Feb-13 5:34 
GeneralRe: One small step for a man..... PinmemberDavid Serrano Martínez12-Feb-13 23:30 
AnswerRe: One small step for a man..... PinmemberSentenryu18-Feb-13 4:00 
GeneralRe: One small step for a man..... Pinmvpgeoyar8-Jul-13 13:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 11 Feb 2013
Article Copyright 2013 by syed_babu
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid