C++11 Smart Pointers

Introduction

In this article I will going to demonstrate general-purpose smart pointers and I going explain how to work with automatic memory management and bounds checking

Background

So, what is a smart pointer and when we should use it?
As we know in C++ programs, if you allocate memory and never deallocate it, you’ve got a memory leak (it’s common problem with dynamically allocated
Consider, then, a function  FuncToAllocateDynamicMemory:

void FuncToAllocateDynamicMemory () {

 std::string *pStr= new std::string("John");//allocate memory

 /*
   Section of code which uses a pStr pointer
 */

 delete pStr;   //release memory
}

This looks good, but there are several ways FuncToAllocateDynamicMemory could fail to delete the ‘pStr’ object. There might be a premature return statement somewhere inside the section of code which uses a pStr pointer. If such a return were executed, control would never reach the delete statement. A similar situation would arise if some statement inside the same code (see 6 line of  code above) might throw an exception. If so, control would again not get to the delete, and we’re going to have memory leak…

It would be nice if, in addition, the memory pointed to by pStr was freed.

The problem of ‘pStr’  is that it is just an ordinary pointer and not a class object having a destructor. If it were an object, we could have its destructor delete the pointed-to memory when the object expires.

And that is the main idea of smart pointers which are crucial for the RAII (Resource Acquisition Is Initialialization) programming idioms

Using Smart Pointers

A smart pointer is a class object that acts like a pointer but has additional features. In this section we are going to look smart pointers that can help with managing the use of dynamic memory allocation.

So,  let’s look at four Smart Pointers from Standard Library:

  • auto_ptr
  • shared_ptr
  • unique_ptr
  • weak_ptr

Each of these smart pointers defines a pointer-like object intended to be assigned an address obtained (directly or indirectly) by “new”. When the smart pointer expires, its destructor uses “delete” to deallocate the memory. So, if you assign an address returned by new to one of these objects, you don’t have to remember to use the ‘delete statement” to free the memory later; It will be freed automatically when the smart pointer object expires.

Steps to create Smart Pointers of Standard Library

To create one of Smart pointers  we should make follow these three steps:

  1. Include the <memory> header file;
    (Smart pointers are defined in the std namespace in the  memory.h)
  2. Replace the pointer-to-T with a smart pointer object that points to T;
    (in our example, T =  std::string)
  3. Remove the delete statement;

New version of FuncToAllocateDynamicMemory function

Let’s show the FuncToAllocateDynamicMemory function with changes from previous item:

#include

void FuncToAllocateDynamicMemory () {
 /*  We can use everyone one of the following pointers:
     std::auto_ptr
     std::shared_ptr
     std::unique_ptr
     std::weak_ptr
 */
  std::auto_ptr pStr (new std::string("John"));

 /*
   Section of code which uses a pStr pointer
 */
}

Here we have shown how to use one of smart pointers (auto_ptr) to prevent FuncTAllocate DynamicMemory’s potential resource leak.  The shared_ptr, unique_ptr and  weak_ptr  share the same behavior in this situation.

The above example demonstrates the two critical aspects of using smart pointers to manage memory:

  1. Resources are acquired and immediately turned over to Smart-pointers. (Above,the   resource returned by operator “new” is used to initialize the auto_ptr that will manage it.)
  2. Smart-pointers use their destructors to ensure that resources are released.  (e.g.,  destructors are called when an object goes out of scope).

Consider smart pointers

std::auto_ptr

Due to the fact that an std::auto_ptr automatically deletes what it points to when the std::auto_ptr is destroyed, it’s very important that there never be more than one auto_ptr pointing to an object. If there were, the object would be deleted more than once, and that would put your program on the fast track to undefined behavior. To avoid such problems, std::auto_ptr have an unusual characteristic: copying them (via copy constructor or copy assignment operator) sets them to null, and the copying pointer assumes sole ownership of the resource!

We will begin with the following example:

auto_ptr_1
As we can see, pSmrtPtr1  points to the object returned from operator ‘new’

auto_ptr_2
The pSmrtPtr2 now points to the object returned from operator ‘new’, but the pSmrtPtr1 is now null

auto_ptr_3
Now pSmrtPtr3 now points to the object returned from operator ‘new’, and pSmrtPtr2 is null.

auto_ptr_4
OOPS!!! Error exception !! That causes ‘pSmrtPtr2’  to no longer refer to the string. After a std::auto_ptr gives up ownership of an object, it no longer provides access to the object. When the program goes to print the string pointed to by  ‘pSmrtPtr2’, it finds the null pointer, which apparently is an unpleasant surprise.

In C++11 deprecates std::auto_ptr and introduces three new smart pointer types: std::unique_ptr, std::shared_ptr,  and  std::weak_ptr.

Note:
The std::auto_ptr  use delete in his destructor, not delete [] !!!  That means that using std::auto_ptr  with dynamically allocated arrays is a bad idea, though, regrettably, one that will compile:
auto_ptr_array
As we can see,  the above code was compiled fine! Of course vector and string can almost always replace dynamically allocated arrays, but if you still think it would be nice to have auto_ptr-like classes for arrays, then try to consider next smart pointer.

std::unique_ptr

The std::unique_ptr is better than std::auto_ptr. Like std::auto-ptr, std::unique_ptr incorporates the ownership model. Yet instead of crashing, the std::unique_ptr version yields a compiletime error (instead of run-time crash) objecting to improper initialization…

Now consider the std::unique_ptr equivalent in the above example:

unique_ptr

As we can see, in this case, the compiler does not allow statements:

  •  std::unique_ptr<std::string> pSmrtPtr2 (pSmrtPtr1);
  •  std::unique_ptr<std::string> pSmrtPtr3 = pSmrtPtr2;

Therefore we avoid the problem of pSmrtPtr2,pSmrtPtr3 and not pointing to valid data.
Hence, std::unique_ptr is safer (compile-time error versus potential program crash) than std::auto_ptr.

But there are cases, when assigning one smart pointer to another doesn’t leave a dangerous orphan behind. Let’s consider the following example:

unique_ptr1
unique_ptr2

Here, Foo() function returns a temporary unique_ptr, and then the returned unique_ptr pointer will be destroyed.

unique_ptr3
unique_ptr4
That’s OK because ‘pSmrtPtr1’ pointer now has ownership of the string object. But another good thing that happened is that the temporary std::unique_ptr returned by  function Foo() is soon destroyed, this prevents possibility of it being misused in an attempt to access invalid data. In brief, if a program attempts to assign one std::unique_ptr to another, the compiler allows it if only the source object is a temporary rvalue and disallows it if the source object has some duration….

This is the output of running the example above:
unique_ptr5

Summary:
(1)  When using unique_ptr, the compiler does not allow statement doing assignment  like this:

std::unique_ptrp1(new std::string("Blahblah"));
std::unique_ptrp2;
p2 = p1;

So, std::unique_ptr is safer than std::auto_ptr (It avoid problem of where pointer not pointing to valid data)

(2)  Also std::unique_ptr has another advantage over std::auto_ptr. It has a variant that can be used with arrays. The std::unique_ptr has a new[], delete[] version:

std::unique_ptr<double[]>pda(new double(3));//will use delete[]

std::shared_ptr

An alternative to std::auto_ptr is a reference-counting smart pointer (RCSP). An RCSP is a smart pointer that keeps track of how many objects point to a particular resource and automatically deletes the resource when nobody is pointing to it any longer.

Suppose we go back to previous example but use std::shared_ptr instead of std::auto_ptr:

#include <iostream>
#include <string>
#include <memory>

int main()
{
   std::shared_ptr<std::string> pSmrtPtr1
                   (new std::string("A string of pSmrtPtr1"));

   std::shared_ptr<std::string> pSmrtPtr2 (pSmrtPtr1);

   std::shared_ptr<std::string> pSmrtPtr3  = pSmrtPtr2;

   std::cout << "The pSmrtPtr3 is " << *pSmrtPtr3 << "!\n";
   std::cout << "The pSmrtPtr2 is " << *pSmrtPtr2 << "!\n";
   std::cout << "The pSmrtPtr1 is " << *pSmrtPtr1 << "!\n";

    return 0;
}

Then the program runs fine and gives this output:
shared_ptr_output

Let’s see how it work:
shared_ptr1
After creating and initializing ‘pSmrtPtr1’ (in the 7th row), it’s point to the string object, and the shared reference count is equal to 1!

shared_ptr2
This time both pSmrtPtr1 and pSmrtPtr2 point to the same object, and the reference count is upped from 1 to 2.

shared_ptr3
This time  pSmrtPtr1, pSmrtPtr2 and pSmrtPtr3 point to the same object, and the reference count (incrementing the strong reference count.) is upped from 2 to 3.

At the end of the program, pSmrtPtr3, which was declared last, is the first
object to have its destructor called. The destructor decreases the reference count to 1.
Then the members of the array of shared_ptrs are freed. The destructor for pSmrtPtr1 decrements the count to 0 and frees the previously allocated space.
So with std::shared_ptr runs fine.

2 Responses to C++11 Smart Pointers

  1. Motaz says:

    thank you very much it is really helpful
    but why don’t you talk about the weak_ptr ?

    • cpp11 says:

      Thank you Motaz for your answer.
      I will try to update all the materials, only but not now …
      Now on my work is started a release cycle of project…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s