35. Smart Pointers – Modern C++ for Absolute Beginners: A Friendly Introduction to C++ Programming Language and C++11 to C++20 Standards

© Slobodan Dmitrović 2020
S. DmitrovićModern C++ for Absolute Beginnershttps://doi.org/10.1007/978-1-4842-6047-0_35

35. Smart Pointers

Slobodan Dmitrović1 
(1)
Belgrade, Serbia
 

Smart pointers are pointers that own the object they point to and automatically destroy the object they point to and deallocate the memory once the pointers go out of scope. This way, we do not have to manually delete the object like it was the case with the new and delete operators.

Smart pointers are declared in the <memory> header. We will cover the following smart pointers – unique and shared.

35.1 Unique Pointer

A unique pointer called std::unique_ptr is a pointer that owns an object it points to. The pointer can not be copied. Unique pointer deletes the object and deallocates memory for it, once it goes out of scope. To declare a unique pointer to a simple int object, we write:
#include <iostream>
#include <memory>
int main()
{
    std::unique_ptr<int> p(new int{ 123 });
    std::cout << *p;
}

This example creates a pointer to an object of type int and assigns a value of 123 to the object. A unique pointer can be dereferenced in the same way we as a regular pointer using the *p notation. The object gets deleted once p goes out of scope, which in this case, is at the closing brace }. No explicit use of delete operator is required.

A better way to initialize a unique pointer is through an std::make_unique<some_type>(some_value) function, where we specify the type for the object in angle brackets and the value for the object pointer points at in parentheses:
#include <iostream>
#include <memory>
int main()
{
    std::unique_ptr<int> p = std::make_unique<int>(123);
    std::cout << *p;
}

The std::make_unique function was introduced in the C++14 standard. Make sure to compile with the -std=c++14 flag to be able to use this function.

We can create a unique pointer that points to an object of a class and then use its -> operator to access object members:
#include <iostream>
#include <memory>
class MyClass
{
public:
    void printmessage()
    {
        std::cout << "Hello from a class.";
    }
};
int main()
{
    std::unique_ptr<MyClass> p = std::make_unique<MyClass>();
    p->printmessage();
}

The object gets destroyed once p goes out of scope. So, prefer a unique pointer to raw pointer and their new-delete mechanism. Once p goes out of scope, the pointed-to object of a class gets destroyed.

We can utilize polymorphic classes using a unique pointer:
#include <iostream>
#include <memory>
class MyBaseClass
{
public:
    virtual void printmessage()
    {
        std::cout << "Hello from a base class.";
    }
};
class MyderivedClass: public MyBaseClass
{
public:
    void printmessage()
    {
        std::cout << "Hello from a derived class.";
    }
};
int main()
{
    std::unique_ptr<MyBaseClass> p = std::make_unique<MyderivedClass>();
    p->printmessage();
}

Neat ha? No need to explicitly delete the allocated memory, the smart pointer does it for us. Hence the smart part.

35.2 Shared Pointer

We can have multiple pointers point to a single object. We can say that all of them own our pointed-to object, that is, our object has shared ownership. And only when last of those pointers get destroyed, our pointed to object gets deleted. This is what a shared pointer is for. Multiple pointers pointing to a single object, and when all of them get out of scope, the object gets destroyed.

Shared pointer is defined as std::shared_ptr<some_type>. It can be initialized using the std::make_shared<some_type>(some_value) function. Shared pointers can be copied. To have three shared pointers pointing at the same object we can write:
#include <iostream>
#include <memory>
int main()
{
    std::shared_ptr<int> p1 = std::make_shared<int>(123);
    std::shared_ptr<int> p2 = p1;
    std::shared_ptr<int> p3 = p1;
}

When all pointers get out of scope, the pointed-to object gets destroyed, and the memory for it gets deallocated.

The main differences between unique and shared pointers are:
  • With unique pointers, we have one pointer pointing at and owning a single object, whereas with shared pointers we have multiple pointers pointing at and owning a single object.

  • Unique pointers can not be copied, whereas shared pointers can.

If you wonder which one to use, let us say that 90% of the time, you will be using the unique pointer. Shared pointers can be used to represent data structures such as graphs.

Smart pointers are class templates themselves, meaning they have member functions. We will just briefly mention they can also accept custom deleters, a code that gets executed when they get out of scope.

Notice that with smart pointers, we do not need to specify the <some_type*>, we just need to specify the <some_type>.

Important!

Prefer smart pointers to raw pointers. With smart pointers, we do not have to worry if we properly matched calls to new with calls to delete as we do not need them. We let the smart pointer do all the heavy lifting.