How not to use Smart Pointers in C++?

In modern C++, std::shared_ptr is a smart pointer that manages shared ownership of a dynamically allocated object. It is part of the C++ Standard Library’s memory header.

Shared pointers keep track of how many objects own a particular resource and automatically delete the resource when there are no owners left. However, if not used carefully, std::shared_ptr can lead to problems such as double-deletion, dangling pointers, and memory leaks.

Let’s discuss these problems one by one,

Avoiding creating multiple shared_ptr with same Raw Pointer

Creating multiple std::shared_ptr objects from the same raw pointer can lead to undefined behavior such as double-deletion or dangling pointers. Let’s see an example

#include <iostream>
#include <memory>

int main()
{
    int* rawPtr = new int(42);
    std::shared_ptr<int> ptr1(rawPtr);
    std::shared_ptr<int> ptr2(rawPtr);  // Problematic!

    // When ptr2 goes out of scope, it will delete rawPtr.
    // Then ptr1 will be left with a dangling pointer.
}

This kind of code will crash your application. Let’s see what is happening in this code,

  1. rawPtr is a raw pointer that points to an integer allocated on the heap.
  2. ptr1 is a shared pointer that takes ownership of the integer.
  3. ptr2 is another shared pointer incorrectly given ownership of the same integer.
  4. When ptr2 goes out of scope, it deletes the integer. ptr1 is now a dangling pointer.
  5. When ptr1 goes out of scope, it will try to delete the already deleted integer, leading to a crash.

Avoiding Creating Smart Pointers with Stack memory

Shared pointers are designed to manage dynamic memory (allocated on the heap). When a std::shared_ptr is created with a pointer to an object on the stack, it leads to undefined behavior.

Example of Incorrect Usage:

#include <iostream>
#include <memory>

int main()
{
    int x = 12;
    std::shared_ptr<int> ptr(&x);  // Problematic!

    // ptr will try to delete memory it does not own when going out of scope.
}

This kind of code will crash your application. Let’s see what is happening in this code,

  1. x is a stack-allocated integer.
  2. ptr is a shared pointer that incorrectly attempts to take ownership of x.
  3. When ptr goes out of scope, it will call delete on a stack-allocated variable, causing a crash.

Using make_shared

To avoid these issues, it is recommended to use std::make_shared. This function template enables the creation of std::shared_ptr objects without directly passing raw pointers, ensuring safe memory allocation and deallocation.

Correct Usage with make_shared:

#include <iostream>
#include <memory>

int main()
{
    auto ptr1 = std::make_shared<int>(42);
    std::shared_ptr<int> ptr2(ptr1); // Copy of ptr1, shares ownership.

    // Both ptr1 and ptr2 co-own the memory safely.
    // Memory will be automatically deleted when the last shared_ptr is destroyed.
}

Advantages of make_shared

  • It creates a shared_ptr in a safe manner, ensuring there is only one owner for the newly allocated memory.
  • It is more efficient because it performs a single heap allocation for the object and the control block used by the shared_ptr.
  • It prevents the creation of shared pointers to stack memory because make_shared always allocates memory on the heap.

Summary

std::shared_ptr is a powerful tool for automatic memory management in C++. It is crucial to follow best practices like using std::make_shared and avoiding sharing raw pointers between multiple shared_ptr instances. By doing so, you ensure safe ownership semantics and prevent common errors like double-deletion and dangling pointers that can lead to crashes or undefined behavior. Remember that smart pointers are designed to make your code safer and more robust, but they still require careful use.

1 thought on “How not to use Smart Pointers in C++?”

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top