Smart Pointers

unique, shared and weak pointer

C++11 Smart Pointers

  • The unique pointer std::unique_ptr (owns a resource exclusively)
  • The shared pointer std::shared_ptr (does not own it, but with internal reference count)
  • The weak pointer std::weak_ptr (shared, but without the reference counter)

Before C++11 introduced those, std::auto_ptr tries to resolve the same (with unusual copy semantics that behaves like move semantics) and is now deprecated and removed in C++17, the std::unique_ptr is preferred.

It’s all about Scope

The shared pointer behaves like a garbage collector, the last one to stop using it will implicitly delete the object. Prefer local objects over using a shared pointer.

shared_ptr Example of assign, reset and copy.

#include <iostream>
#include <memory>

using std::cout;
using std::endl;

void smartReset(std::shared_ptr<int>& sharedIndex){
  cout << "smartReset() sharedIndex.use_count(): " << sharedIndex.use_count() << endl;
  // magic: reset replaces the managed object 
  sharedIndex.reset(new int(256));
  cout << "smartReset() sharedIndex.use_count(): " << sharedIndex.use_count() << endl;
  cout << "smartReset() sharedIndex: " << *sharedIndex << endl;
}

void smartInc(std::shared_ptr<int>& sharedIndex){
  *sharedIndex += 128;
  // smartInc() sharedIndex: 384
  cout << "smartInc() sharedIndex: " << *sharedIndex << endl;
}

int main() {
  auto sharedIndex = std::make_shared<int>(42);
  // sharedIndex: 42 
  cout << "sharedIndex: " << *sharedIndex << endl;
  // Copy 
  auto secondSharedIndex = sharedIndex;
  // sharedIndex.use_count(): 2
  cout << "sharedIndex.use_count(): " << sharedIndex.use_count() << endl;
  smartReset(sharedIndex);  
  smartInc(sharedIndex);  
  cout << "secondSharedIndex.use_count(): " << secondSharedIndex.use_count() << endl;
  // secondSharedIndex: 42
  cout << "secondSharedIndex: " << *secondSharedIndex << endl;
  // sharedIndex: 384
  cout << "sharedIndex: " << *sharedIndex << endl;
}

Moving smart pointers

When we transfer ownership from one object to an object of another class, we can use “move semantics”. Depending on the case, constant references with move can be faster and safer even in multi-threaded environments, since move does not rely on a reference count of a shared_pr.

In this example, move also makes it clear that passing ownership is intended:

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  // move, instead of: Invocation = Value;
  Invocation = std::move(Value);
}

The move semantics allows, for example, moving an object to another object (and scope) without an ownership relation, i.e. the “origin” scope is no longer responsible for memory allocation and deallocation.

Exclusive Ownership

In RAII there is single and shared ownership, or in implementation unique_ptr vs. shared_ptr. Most RAII classes have unique ownership, such as std::vector.

Ownership is also bound to the scope and lifetime of the variable. A unique_ptr clears itself when it falls out of scope. When a shared_ptr falls out of scope, the references are decremented and cleaned up when the reference count is 0 (which causes their own destruction).

Smartpointer rules

C++ Core Guidelins of Smart pointers regarding ownership and passing parameters to functions (R.30-37).

Written on March 28, 2021
[ c++  ]