Everything Smart About Smart Pointers — Part 1 : Motivation

Gagan Saini
The Startup
Published in
3 min readSep 25, 2020

What’s wrong with the regular not-so-smart pointers?

How do we use regular pointers in C++?

  • We store the address of a local variable in a pointer variable and then manipulate the variable through that pointer.
  • Or we allocate some memory on heap using ‘new’ and store the address of that memory in some pointer variable. Then we refer to this memory location through this pointer variable. And when we no longer need it, we free the memory using ‘delete’.

Sounds simple. What can go wrong with this approach. Let’s look at the following example:

void f()
{
ClassA* ptr = new ClassA; // create an object explicitly
… // perform some operations
delete ptr; // clean up (destroy the object explicitly)
}
  • What if there is a return statement before the ptr deletion is happening
  • An exception might occur in the code before the delete statement. Such an exception would exit the function immediately, without executing the delete statement at the end of the function. Obviously, we can use try catch block here. But that might make the code complicated and redundant in some cases.

These situations lead to memory leaks — where we are unable to free the memory which is no longer needed by our program.

Other inherent problems with raw pointers are:

Who owns the memory?

We have a pointer variable. What do we do after this variable goes out of scope. Do we deallocate the memory pointed to by it. What if there are more copies of it. What if we delete it and other parts of the program still need this memory, they will get memory related errors (segmentation faults to be specific) if they try to access the deleted memory.

In other words, we have no means to tell if a pointer variable owns the memory or if it’s just a copy of the another pointer which is declared elsewhere.

vector<Employee *> employees;
void createAndAddEmployee(string name)
{
Employee *emp = new Employee(name);
employees.push_back(emp);
//other operations....
//...
//emp pointer variable going out of scope on function exit
//do we delete the memory pointed to by it????
}

In the above example, we don’t want to deallocate the memory as the owner of this memory is the employees vector.

How should we deallocate the memory?

We can deallocate memory in one of the following ways in C++

  • using ‘delete’ which in-turn calls the destructor of the object.
  • using ‘delete[]’ if the pointer points to an array of objects. It calls destructors of each objects in the array.
  • In some cases, we may have some dedicated function that destroys the pointer passed to it.

With only the pointer in hand, we cannot tell which of the above technique is to be used for deleting the pointer. As we cannot be sure if the pointer points to an array or just a single object.

Multiple Destructions

We don’t have any way to ensure that we perform the destruction exactly once along every path in the code. Destroying the same memory more than once leads to undefined behavior.

Dangling pointers

Dangling pointers point to the objects that have already been destroyed. Given a pointer, we cannot tell if it is dangling or not just by looking at it.

Smart pointers to the rescue

Smart pointers are wrappers around the raw pointers that address the issues discussed above. We have following four smart pointers in C++

  1. std::unique_ptr
  2. std::shared_ptr
  3. std::weak_ptr
  4. std::auto_ptr — deprecated.

In next parts of the series, we’ll discuss these pointers one by one in detail.

--

--