Shared Pointer

  • std::shared_ptr was introduced in C++11

    • Different instances can share the same memory allocation
    • It uses reference counting
  • When a shared_ptr object is copied or assigned

    • There are no memory operations
    • Instead, the reference counter is incremented
    • When a copy is destroyed, the counter is decremented
    • When the last copy is destroyed, the counter is equal to zero
    • The allocated memory is then released
  • std::shared_ptr Structure

  • std::shared_ptr has two private data members

    • A pointer to the allocated memory
    • A pointer to its "control block"
      • The control block contains the reference counter
  • std::shared_ptr is defined in <memory>

  • To create a shared_ptr object

// Pass a pointer as the constructor argument
std::shared_ptr<int> ptr1(new int(42));

// Calling std::make_shared() is better
auto ptr2 = std::make_shared<int>(42);

Copying std::shared_ptr

  • Copy constructor std::shared_ptr<int> ptr3 = ptr2;

  • Before the copy

    • ptr2's reference counter has the value 1
  • After the copy

    • ptr2 and ptr3 share the same memory pointer and control block
    • The counter in the shared control block has the value 2

std::shared_ptr Operations

  • shared_ptr supports the same operations as unique_ptr
    • Including dereferencing
auto shptr = make_shared<int>(42);
std::cout << "shared_ptr's data is" << *shptr << '\n';
  • Plus copy assignment
auto shptr2 = shptr;
std::cout << "Copied shared_ptr's data is" << *shptr2 << '\n';

std::shared_ptr<int> ptr3;
shptr3 = shptr;
std::cout << "Assigned shared_ptr's data is" << *shptr3 << '\n';

shared_ptr vs unique_ptr

  • unique_ptr has the same overhead as using a traditional pointer
  • shared_ptr has more overhead
    • Control block initialization
    • The reference counter is updated on every copy, assignment, move operation or destructor call
  • Only use shared_ptr when necessary

Threads and std::shared_ptr

  • Two potential issues

  • The reference counter

    • Modified by every copy, assignment, move operation or destructor call
    • Conflicting accesses by concurrent Threads
  • The pointed-to data

    • Threads could dereference std::shared_ptr concurrently
    • Conflicting accesses
  • The reference counter is an atomic type

    • This makes it safe to use in threaded programs
    • No extra code required when copying, moving or assigning
    • Adds extra overhead in single threaded programs
    • Internal synchronization
  • The pointed to data is the responsibility of the programmer

    • Must be protected against data races
    • Concurrent accesses to the data must be synchronized
    • C++20 has std::atomic<std::shared_ptr>
    • External synchronization