Summary: In this tutorial, we will learn what smart pointers are, types of smart pointers and why we should use a smart pointer instead of a raw pointer in C++.

Introduction to Smart Pointers in C++

In computer science, a smart pointer is an abstract data type that simulates a pointer while providing added features, such as automatic memory management or bounds checking. Such features are intended to reduce bugs caused by the misuse of pointers, while retaining efficiency.

Wikipedia

A smart pointer is an object that acts as a wrapper around the raw pointer in C++.

Smart pointer like a raw pointer point to objects but is more enhanced in terms of functionality.

Smart Pointers keep track of the objects they point to and automatically deletes them when they are no longer in use (when the last (or only) owner of an object is destroyed).

Under the hood smart pointer performs delete action on the wrapped raw pointer.

Why Should we use Smart Pointers?

In C++, the pointer objects created using the new operator is allocated memory in the heap.

C++ does not automatically free up the memory allocated in heap because it does not support automatic garbage collection.

It is the job of a programmer to free up space using the delete operator when a pointer is no longer in use or goes out of scope.

If we do not do so then we may end up creating a memory leak.

In these cases, the smart pointer comes handy because it takes care of the memory management and clears the memory in the heap for us.

Types of Smart Pointers

There are 3 types of smart pointers:

  1. Unique Pointer (unique_ptr)
  2. Shared Pointer (smart_ptr)
  3. Weak Pointer (weak_ptr)

Note: Auto pointer (auto_ptr) has been depreciated. And to use smart pointers memory header file must be included as it contains the necessary class and member methods used by the smart pointers.

Unique Pointer

A unique pointer is the type of smart pointer that owns an object to which it points i.e. two unique pointers can not point to the same object.

Syntax to declare the unique pointer:

std::unique_ptr<T> ptr;

where T is the type of object that the unique pointer will manage.

Initialization of Unique Pointer:

std::unique_ptr<T> ptr { new T(value) };
//OR
std::unique_ptr<T> ptr = make_unique<T>(value);

Note: make_unique<T> is a function available since C++14. It returns a unique pointer of the specified type and allows us to pass the initialization value to the constructor of the managed object.

Properties of Unique Pointer

  • Points to any object of type T on the heap.
  • There can be only one unique_ptr<T> pointing to a single object on the heap. Hence no other pointer can point to this object (i.e Owns what it points to).
  • Cannot be assigned or copied.
  • Can be moved.
  • When pointer is destroyed, what it points to automatically get destroyed.

C++ Example using Unique Pointers

#include <iostream>
#include <memory> //necessary for using header file
using namespace std;
 
class Account{
public:
    double balance;
    //Default Constructor
    Account(){
        cout << "Constructor \n";
    }
 
    //Parameterized Constructor
    Account(double bal){
        balance = bal;
        cout << "Constructor \n";
    }
    //Destructor
    ~Account(){
        cout << "Destructor \n";
    }
};
 
int main()
{
    cout << "Primitive Raw Pointer" << endl;
    //pointer will be created and destroyed inside scope
    {
        //integer pointer of primitive data type
        int *intPtr {new int(2)};
        cout << "Value of Integer Pointer is: "<< *intPtr <<endl;
    }
 
    cout << "-----------------------------------------"<<endl;
 
    cout << "Primitive Smart Pointer" << endl;
    //pointer will be created and destroyed inside scope
    {
        //integer pointer of primitive data type
        unique_ptr<int> intPtr {new int(2)};
        cout << "Value of Integer Pointer is: "<< *intPtr <<endl;
    }
 
    cout << "-----------------------------------------"<<endl;
 
    cout << "Object Raw Pointer" << endl;
    //pointer will be created and destroyed inside scope
    {
 
        //Object pointer
        Account *accPtr {new Account(800)};
        cout << "Balance: "<< accPtr->balance <<endl;
 
    }
 
    cout << "-----------------------------------------"<<endl;
 
    cout << "Object Smart Pointer" << endl;
    //pointer will be created and destroyed inside scope
    {
 
        //Object pointer
        unique_ptr<Account> accPtr {new Account(900)};
        cout << "Balance: "<< accPtr->balance <<endl;
 
    }
 
    return 0;
}

Output:

Smart pointer in C++

Observe the output, In the case of the raw object pointer, the destructor of the Account class was not invoked.

Whereas, in the case of smart object pointer, the destructor is automatically invoked as soon as the object goes out of the scope (i.e. object is automatically destroyed).

It proves that smart pointers automatically calls the underlying destructor method for the object to free up the memory.

Shared Pointer

A Shared Pointer like other pointers manages an object in the heap memory. However, unlike the unique pointer, the pointed object can be shared among other shared pointers.

Each shared pointer has a use_count() method associated with it, which returns the number of shared pointers managing the heap object.

Syntax to Declare Shared Pointer:

std::shared_ptr<T> ptr;

where T is the type of object that the smart pointer will manage.

Initialization of Shared Pointer:

std::shared_ptr<T> ptr { new T(value) };
//OR
std::shared_ptr<T> ptr = make_shared<T>(value);

Properties of Shared Pointer

  • Points to an object of type T on the heap memory.
  • There can be many shared pointers pointing to the same object on the heap (There is a shared ownership relationship).
  • Can be assigned, copied, and moved.

C++ Example using Shared Pointers

#include <iostream>
#include <memory> //necessary for using header file
using namespace std;
 
class Account{
public:
    double balance;
    //Default Constructor
    Account(){
        cout << "Constructor \n";
    }
 
    //Parameterized Constructor
    Account(double bal){
        balance = bal;
        cout << "Constructor \n";
    }
    //Destructor
    ~Account(){
        cout << "Destructor \n";
    }
};
 
int main()
{
    cout << "Primitive Raw Pointer" << endl;
    //pointer will be created and destroyed inside scope
    {
        //integer pointer of primitive data type
        int *intPtr {new int(2)};
        cout << "Value of Integer Pointer is: "<< *intPtr <<endl;
    }
 
    cout << "-----------------------------------------"<<endl;
 
    cout << "Primitive Smart Pointer" << endl << endl;
    //pointer will be created and destroyed inside scope
    {
        //integer pointer of primitive data type
        shared_ptr<int> intPtr {new int(2)};
        cout << "Number of sharing: " << intPtr.use_count() <<endl;
        cout << "Value of 1st Pointer is: "<< *intPtr <<endl;
 
        {
            //sharing pointer
            shared_ptr<int> intPtr2 {intPtr};
            cout << "Number of sharing: " << intPtr.use_count() <<endl;
            cout << "Value of 2nd Pointer is: "<< *intPtr <<endl;
        }
 
        cout << "Number of sharing: " << intPtr.use_count() <<endl;
    }
 
    cout << "-----------------------------------------"<<endl;
 
    cout << "Object Raw Pointer" << endl << endl;
    //pointer will be created and destroyed inside scope
    {
        //Object pointer
        Account *accPtr {new Account(800)};
        cout << "Balance: "<< accPtr->balance <<endl;
    }
 
    cout << "-----------------------------------------"<<endl;
 
    cout << "Object Smart Pointer" << endl << endl;
    //pointer will be created and destroyed inside scope
    {
        //Object pointer
        shared_ptr<Account> accPtr {make_shared<Account>(900)};
        cout << "Balance: "<< accPtr->balance <<endl;
    }
    return 0;
}

Output:

Unique, shared and weak smart Pointers in C++

Observe the output part of the primitive smart pointer, the count value increments to the value 2 when intPtr is shared with intPtr2 and decreases back to 1, when the second shared pointer (intPtr2) is destroyed because of the scope.

In the case of object raw pointer, the destructor of Account class is not invoked.

However, the shared pointer smartly invokes the destructor as soon as the pointer goes out of scope.

Weak Pointer

Weak Pointer is another type of smart pointers in C++ that points to an object in the heap memory. However, unlike other types of smart pointers, it doesn’t participate in the owning relationship.

Weak pointer uses a shared pointer to manage objects but does not maintain the counter.

Its major aim is to maintain the weak relationships among objects to prevent a deadlock kind of situation.

Refer strong reference vs weak reference for details

Syntax to Declare Weak Pointer:

weak_ptr<T> ptr;

Properties of Weak Pointer:

  • Points to an object of type T on the heap.
  • Doesn’t participate in owning relationship.
  • Always created from a shared_ptr.
  • Does not increment or decrement reference use count.
  • Avoid strong reference cycles, which could prevent objects from being deleted.

That’s all for smart pointers in C++, next we will discuss in deep about strong reference cycles and custom deleters in C++, till then if you have any doubt then comment below.

Leave a Reply