🔥 $100K Hit! Where Will Bitcoin Go Next? Find Out Live!

Code has been added to clipboard!

How to Use Smart Pointers for C++ Memory Management

Reading time 4 min
Published Oct 17, 2019
Updated Oct 17, 2019

A C++ smart pointer is a wrapper class. While the objects of this class look like regular pointers, they have additional functionalities – e.g., reference counting, automatic object destruction, and C++ memory management.

Memory leaks: how do these happen?

In C++, you can reach a variable by using either their name or address which represents the unique location of the variable’s value in the system memory. A C++ pointer is a variable that contains the address of another variable as its value. It can also be called a raw pointer.

While these pointers are very convenient to use, they might be hard to manage. Each object that you dynamically allocate using the operator new must then be manually deallocated with the operator delete:

Example
char* str = new char [5];
delete [] str;

If you fail to deallocate memory, C++ throws a risk of a memory leak. A single leak might just cost you a few bytes. Unfortunately, these losses add up – e.g., repeating a function that causes a memory leak of five bytes a million times will waste five million bytes. In time, this can crash your application.

Tip: it can be easy to miss a single leak, but it’s always better to catch them in time. One of the best options on how to check for memory leaks is using C++ debuggers.

How to use different C++ smart pointer types

To easily deal with the risk of memory leaks, you can use smart pointers. First and foremost, it simplifies the C++ dynamic memory allocation by making deallocation automatic. This means you do not need to manually use the delete operator each time.

There are four types of smart pointers in C++:

Smart pointer Best used when
std::unique_ptr You don’t need to hold multiple references to a single object
std::shared_ptr You need to hold multiple references to a single object
std::weak_ptr You need to hold multiple references to a single object but don’t want to deallocate the object
std::auto_ptr Deprecated – use std::unique_ptr instead
DataCamp
Pros
  • Easy to use with a learn-by-doing approach
  • Offers quality content
  • Gamified in-browser coding experience
  • The price matches the quality
  • Suitable for learners ranging from beginner to advanced
Main Features
  • Free certificates of completion
  • Focused on data science skills
  • Flexible learning timetable
Udacity
Pros
  • Simplistic design (no unnecessary information)
  • High-quality courses (even the free ones)
  • Variety of features
Main Features
  • Nanodegree programs
  • Suitable for enterprises
  • Paid Certificates of completion
edX
Pros
  • A wide range of learning programs
  • University-level courses
  • Easy to navigate
  • Verified certificates
  • Free learning track available
Main Features
  • University-level courses
  • Suitable for enterprises
  • Verified certificates of completion

For a unique reference: std::unique_ptr

Keeping C++ smart pointers unique is a recommended practice, as it keeps the program logic clean and simple. When using the std::unique_ptr smart pointer, you can only assign one owner for the pointer behind the wrapper. The object the std::unique_ptr points to is deleted automatically when the smart pointer leaves the scope.

Example
// Syntax to follow:
std::unique_ptr<data_type> p(new data_type);

// A basic example:
std::unique_ptr<int> p(new int);

The std::unique_ptr smart pointer does not have a copy constructor, so you cannot share or duplicate it – the system will throw an error, just like in the example below:

Example
std::unique_ptr<int> p1(new int(365));
std::unique_ptr<int> p2 = p1;

Note: instead of copying, you can move std::unique_ptr to a new owner.

For shared ownership: std::shared_ptr

Using the std::shared_ptr smart pointer means you can apply multiple owners to a single raw pointer. All of the owners must also leave the scope for it to be deleted.

Example
// Syntax to follow:
std::shared_ptr<data_type> p(new data_type);

// A basic example:
std::shared_ptr<int> p1(new int);

The std::shared_ptr smart pointer can also be used for reference counting. It contains an internal counter which tracks the amount of owners not yet destroyed. To check how many pointers lead to the same address, use the use_count() method:

Example
std::cout << p.use_count() << std::endl;

In case of a cycle: std::weak_ptr

There is a type of C++ memory leak called the circular reference, also known as a cycle. It happens when C++ smart pointers form a referential loop by the last object in the reference chain points to the first one (e.g., X points to Y, Y points to Z, and Z points to X):

Smart Pointer C++

To solve this problem, you need to create a std::weak_ptr smart pointer within the std::shared_ptr smart pointer, just as you see in the example below:

Example
std::shared_ptr<char> p_shared = std::make_shared<char>(15);
std::weak_ptr<char> p_weak1(p_shared);
std::weak_ptr<char> p_weak2(p_weak1);

Both std::weak_ptr and std::shared_ptr smart pointers will point to the same data. However, the std::weak_ptr one will not change the value of the internal counter and hence take part in reference counting. It is also not considered as an owner.

One more reason to use std::weak_ptr for C++ memory management is that it helps with dangling pointers (those that point to deleted data). You can check if a particular piece of data is valid by using lock() or expired():

Example
if(auto tmp = weak1.lock())
  std::cout << *tmp << '\n';
else
  std::cout << "Sorry, weak1 is no longer valid!\n";

if(auto tmp = weak2.lock())
  std::cout << *tmp << '\n';
else
   std::cout << "Sorry, weak2 is is no longer valid!\n";