COMP6771 Week 5.1

Smart Pointers

Object lifetimes

To create safe object lifetimes in C++, we always attach the lifetime of one object to that of something else

  • Named objects:
    • A variable in a function is tied to its scope
    • A data member is tied to the lifetime of the class instance
    • An element in a std::vector is tied to the lifetime of the vector
  • Unnamed objects:
    • A heap object should be tied to the lifetime of whatever object created it
    • Examples of bad programming practice
      • An owning raw pointer is tied to nothing
      • A C-style array is tied to nothing
  • Strongly recommend watching the first 44 minutes of Herb Sutter's cppcon talk "Leak freedom in C++... By Default"

Creating a safe* pointer type

// myintpointer.h

class MyIntPointer {
 public:
  // This is the constructor
  MyIntPionter(int* value);

  // This is the destructor
  ~MyIntPointer() noexcept;

 int* GetValue();

 private:
  int* value_;
};
// myintpointer.cpp
#include "myintpointer.h"

MyIntPointer::MyIntPointer(int* value): value_{value} {}

int* MyIntPointer::GetValue() {
  return value_
}

MyIntPointer::~MyIntPointer() noexcept {
  // Similar to C's free function.
  delete value_;
}

Don't use the new / delete keyword in your own code

We are showing for demonstration purposes

void fn() {
  // Similar to C's malloc
  MyIntPointer p{new int{5}};
  // Copy the pointer;
  MyIntPointer q{p.GetValue()};
  // p and q are both now destructed.
  // What happens?
}

Smart Pointers

  • Ways of wrapping unnamed (i.e. raw pointer) heap objects in named stack objects so that object lifetimes can be managed much easier
  • Introduced in C++11
  • Usually two ways of approaching problems:
    • unique_ptr + raw pointers ("observers")
    • shared_ptr + weak_ptr/observer_ptr
Type Shared ownership Take ownership
std::unique_ptr<T> No Yes
raw pointers No No
std::shared_ptr<T> Yes Yes
std::weak_ptr<T> No No

Unique pointer

  • std::unique_pointer<T>
    • The unique pointer owns the object
    • When the unique pointer is destructed, the underlying object is too
  • std::experimental::observer_ptr<T>
    • Unique Ptr may have many observers
    • This is an appropriate use of raw pointers (or references) in C++
    • Once the original pointer is destructed, you must ensure you don't access the raw pointers (no checks exist)
    • These observers do not have ownership of the pointer

Unique pointer: Usage

#include <memory>
#include <iostream>

int main() {
  std::unique_ptr<int> up1{new int};
  std::unique_ptr<int> up2 = up1; // no copy constructor
  std::unique_ptr<int> up3;
  up3 = up2; // no copy assignment
  
  up3.reset(up1.release()); // OK
  std::unique_ptr<int> up4 = std::move(up3); // OK
  std::cout << up4.get() << "\n";
  std::cout << *up4 << "\n";
  std::cout << *up1 << "\n";
}

Can we remove "new" completely?

Observer Ptr: Usage

#include <memory>
#include <experimental/memory>
#include <iostream>

int main() {
  int *i = new int;
  std::unique_ptr<int> up1{i};
  *up1 = 5;
  std::cout << *up1 << "\n";
  std::experimental::observer_ptr<int> op1{i};
  *op1 = 6;
  std::cout << *op1 << "\n";
  up1.reset();
  std::cout << *op1 << "\n";
}

Unique Ptr Operators

#include <memory>
#include <experimental/memory>
#include <iostream>

int main() {
  // 1 - Worst
  int *i = new int;
  std::unique_ptr<std::string> up1{i};
 
  // 2 - Not good
  std::unique_ptr<std::string> up2{new std::string{"Hello"}};

  // 3 - Good
  std::unique_ptr<std::string> up3 = make_unique<std::string>("Hello");

  std::cout << *up3 << "\n";
  std::cout << *(up3.get()) << "\n";
  std::cout << up3->size();
}

Shared pointer

  • std::shared_pointer<T>
  • Several shared pointers share ownership of the object
    • A reference counted pointer
    • When a shared pointer is destructed, if it is the only shared pointer left pointing at the object, then the object is destroyed
    • May also have many observers
      • Just because the pointer has shared ownership doesn't mean the observers should get ownership too - don't mindlessly copy it
  • std::weak_ptr<T>
    • Weak pointers are used with share pointers when:
      • You don't want to add to the reference count
      • You want to be able to check if the underlying data is still valid before using it.

Shared pointer: Usage

#include <memory>
#include <iostream>

int main() {
  int* i = new int;
  *i = 5;
  std::shared_ptr<int> x{i};
  std::shared_ptr<int> y = x; // Both now own the memory
  std::cout << "use count: " << x.use_count() << "\n";
  std::cout << "value: " << *x << "\n";
  x.reset(); // Memory still exists, due to y.
  std::cout << "use count: " << y.use_count() << "\n";
  std::cout << "value: " << *y << "\n";
  y.reset(); // Deletes the memory, since
  // no one else owns the memory
  std::cout << "use count: " << x.use_count() << "\n";
  std::cout << "value: " << *y << "\n";
}

Can we remove "new" completely?

Weak Pointer: Usage

#include <memory>
#include <iostream>

int main() {
  std::shared_ptr<int> x = std::make_shared<int>(1);
  std::weak_ptr<int> wp = x; // x owns the memory
  {
    std::shared_ptr<int> y = wp.lock(); // x and y own the memory
    if (y) {
      // Do something with y
      std::cout << "Attempt 1: " << *y << '\n';
    }
  } // y is destroyed. Memory is owned by x
  x.reset(); // Memory is deleted
  std::shared_ptr<int> z = wp.lock(); // Memory gone; get null ptr
  if (z) {
    // will not execute this
    std::cout << "Attempt 2: " << *z << '\n';
  }
}

When to use which type

  • Unique pointer vs shared pointer
    • You almost always want a unique pointer over a shared pointer
    • Use a shared pointer if either:
      • You have several pointers, and you don't know which one will stay around the longest
      • You need temporary ownership (outside scope of this course)

When to use which type

  • Let's look at an example:
    • //lectures/week5/reader.cpp

Shared or unique pointer?

  • Computing examples
    • Linked list
    • Doubly linked list
    • Tree
    • DAG (mutable and non-mutable)
    • Graph (mutable and non-mutable)
    • Twitter feed with multiple sections (eg. my posts, popular posts)
  • Real-world examples
    • The screen in this lecture theatre
    • The lights in this room
    • A hotel keycard
    • Lockers in a school

COMP6771 19T2 - 5.1 - Smart Pointers

By cs6771

COMP6771 19T2 - 5.1 - Smart Pointers

  • 872