Toy

ToyPtr.h

#ifndef TOYPTR_H_
#define TOYPTR_H_

template <typename T> class ToyPtr {
 public:
  // Constructor that accepts the ptr.
  ToyPtr(T* ptr) : ptr_(ptr) { }
  // Destructor that deletes the ptr.
  ~ToyPtr() { delete ptr_; }  // reminder: delete nullptr is safe (C++11)

  // Implement the "*" operator
  T& operator*() { return *ptr_; }
  // Implement the "->" operator
  T* operator->() { return ptr_; }

 private:
  // The pointer itself.
  T* ptr_;
};

#endif  // TOYPTR_H_

usetoy.cc

#include <iostream>

#include "ToyPtr.h"

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

// Simple struct to illustrate "->" operator.
typedef struct Pt_st { int x = 1, y = 2; } Point;

ostream& operator<<(ostream& out, const Point& rhs) {
  return out << "(" << rhs.x << "," << rhs.y << ")";
}

int main(int argc, char** argv) {
  // Create a dumb pointer.
  Point* leak = new Point;

  // Create a "smart" pointer.  (OK, it's still pretty dumb.)
  ToyPtr<Point> notleak(new Point);
  ToyPtr<Point> test(nullptr);  // nothing bad happens when destructed!

  // Use them.
  cout << "     *leak: " << *leak << endl;
  cout << "   leak->x: " << leak->x << endl;
  cout << "  *notleak: " << *notleak << endl;
  cout << "notleak->x: " << notleak->x << endl;

  // Return, leaking "leak" but not "notleak".
  return EXIT_SUCCESS;
}

toyuse.cc

#include <cstdlib>

#include "ToyPtr.h"

// We want two pointers that point to the same thing!
int main(int argc, char** argv) {
  ToyPtr<int> x(new int(5));
  ToyPtr<int> y = x;
  return EXIT_SUCCESS;
}

unique

unique.cc

#include <iostream>  // for std::cout, std::endl
#include <memory>    // for std::unique_ptr
#include <cstdlib>   // for EXIT_SUCCESS

void Leaky() {
  int *x = new int(5);  // heap-allocated
  (*x)++;
  std::cout << *x << std::endl;
}  // never used delete, therefore leak

void NotLeaky() {
  std::unique_ptr<int> x(new int(5));  // wrapped, heap-allocated
  (*x)++;
  std::cout << *x << std::endl;
}  // never used delete, but no leak

int main(int argc, char **argv) {
  Leaky();
  NotLeaky();
  return EXIT_SUCCESS;
}

uniquefail.cc

#include <cstdlib>   // for EXIT_SUCCESS

#include <memory>    // for std::unique_ptr

using std::unique_ptr;

int main(int argc, char** argv) {
  unique_ptr<int> x(new int(5));

  // fail, no copy constructor
  unique_ptr<int> y(x);

  // succeed, z starts with NULL pointer
  unique_ptr<int> z;

  // fail, no assignment operator
  z = x;

  return EXIT_SUCCESS;
}

uniquepass.cc

#include <cstdlib>   // for EXIT_SUCCESS

#include <memory>    // for std::unique_ptr
#include <iostream>  // for std::cout, std::endl

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

int main(int argc, char **argv) {
  unique_ptr<int> x(new int(5));
  cout << "x: " << x.get() << endl;

  unique_ptr<int> y(x.release());  // y takes ownership, x abdicates it
  cout << "x: " << x.get() << endl;
  cout << "y: " << y.get() << endl;

  unique_ptr<int> z(new int(10));

  // y transfers ownership of its pointer to z.
  // z's old pointer is delete'd in the process.
  z.reset(y.release());

  return EXIT_SUCCESS;
}

uniquearray.cc

#include <cstdlib>  // for EXIT_SUCCESS

#include <memory>   // for std::unique_ptr

using std::unique_ptr;

int main(int argc, char** argv) {
  // x is a unique_ptr that holds the address of an array of 5 ints
  unique_ptr<int[]> x(new int[5]);

  x[0] = 1;
  x[2] = 2;

  return EXIT_SUCCESS;
}

shared

shared.cc

#include <cstdlib>   // for EXIT_SUCCESS

#include <iostream>  // for std::cout, std::endl
#include <memory>    // for std::shared_ptr


int main(int argc, char** argv) {
  std::shared_ptr<int> x(new int(10));
  // x contains a pointer to an int and has reference count 1.
  std::cout << x.use_count() << std::endl;

  // temporary inner scope (!)
  {
    // x and y now share the same pointer to an int, and they
    // share the reference count; the count is 2.
    std::shared_ptr<int> y(x);
    std::cout << y.use_count() << std::endl;
  }
  // y fell out of scope and was destroyed.  Therefore, the
  // reference count, which was previously seen by both x and y,
  // but now is seen only by x, is decremented to 1.
  std::cout << x.use_count() << std::endl;

  return EXIT_SUCCESS;
}

sharedvec.cc

#include <cstdlib>   // for EXIT_SUCCESS

#include <iostream>  // for std::cout, std::endl
#include <memory>    // for std::shared_ptr
#include <vector>    // for std::vector

using std::shared_ptr;
using std::vector;
using std::cout;

int main(int argc, char** argv) {
  vector<shared_ptr<int>> vec;

  vec.push_back(shared_ptr<int>(new int(9)));
  vec.push_back(shared_ptr<int>(new int(5)));
  vec.push_back(shared_ptr<int>(new int(7)));

  // z is (a reference to) the (int pointed to by the shared_ptr
  // in vec[1])
  int& z = *vec[1];
  cout << "z is: " << z;
  cout << " with ref count: " << vec[1].use_count() << std::endl;

  shared_ptr<int> copied = vec[1];  // works!
   cout << "*copied: " << *copied;
   cout << " with ref count: " << vec[1].use_count() << std::endl;

  // removes the last element of the vector and deallocates it (7)
  vec.pop_back();

  return EXIT_SUCCESS;
}

sharedcycle.cc

#include <cstdlib>  // for EXIT_SUCCESS

#include <memory>   // for std::shared_ptr

using std::shared_ptr;

struct A {
  shared_ptr<A> next;
  shared_ptr<A> prev;
};

int main(int argc, char **argv) {
  shared_ptr<A> head(new A());
  head->next = shared_ptr<A>(new A());
  head->next->prev = head;

  return EXIT_SUCCESS;
}

weak

weak.cc

#include <cstdlib>   // for EXIT_SUCCESS

#include <iostream>  // for std::cout, std::endl
#include <memory>    // for std::shared_ptr, std::weak_ptr

using std::shared_ptr;
using std::weak_ptr;
using std::cout;
using std::endl;

int main(int argc, char** argv) {
  weak_ptr<int> w;

  {  // temporary inner scope
    shared_ptr<int> y(new int(10));
    w = y;  // assignment of weak_ptr takes a shared_ptr
    shared_ptr<int> x = w.lock();  // "promoted" shared_ptr

    cout << *x << " " << w.expired() << endl;
  }
  cout << w.expired() << endl;
  w.lock();  // returns a nullptr

  return EXIT_SUCCESS;
}

weakcycle.cc

#include <cstdlib>  // for EXIT_SUCCESS

#include <memory>   // for std::shared_ptr, std::weak_ptr

using std::shared_ptr;
using std::weak_ptr;

struct A {
  shared_ptr<A> next;
  weak_ptr<A> prev;
};

int main(int argc, char **argv) {
  shared_ptr<A> head(new A());
  head->next = shared_ptr<A>(new A());
  head->next->prev = head;

  return EXIT_SUCCESS;
}

Makefile

CXX = g++
CPPFLAGS = -Wall -g -std=c++17
PROGS = usetoy toyuse unique uniquepass uniquearray shared sharedvec sharedcycle weakcycle weak

# default target builds all executables
all: $(PROGS)

# ToyPtr example program
usetoy: usetoy.cc ToyPtr.h
    $(CXX) $(CPPFLAGS) -o $@ $<

# ToyPtr example bug
toyuse: toyuse.cc ToyPtr.h
    $(CXX) $(CPPFLAGS) -o $@ $<

# unique_ptr leaky vs. notleaky examples
unique: unique.cc
    $(CXX) $(CPPFLAGS) -o $@ $<

# unique_ptr passing ownership examples
uniquepass: uniquepass.cc
    $(CXX) $(CPPFLAGS) -o $@ $<

# unique_ptr and array pointer example
uniquearray: uniquearray.cc
    $(CXX) $(CPPFLAGS) -o $@ $<

# shared_ptr usage example
shared: shared.cc
    $(CXX) $(CPPFLAGS) -o $@ $<

# shared_ptr in vector container example
sharedvec: sharedvec.cc
    $(CXX) $(CPPFLAGS) -o $@ $<

# shared_ptr in a reference counting cycle
sharedcycle: sharedcycle.cc
    $(CXX) $(CPPFLAGS) -o $@ $<

# weak_ptr in a reference counting cycle
weakcycle: weakcycle.cc
    $(CXX) $(CPPFLAGS) -o $@ $<

# weak_ptr dangling reference example
weak: weak.cc
    $(CXX) $(CPPFLAGS) -o $@ $<

# phony target - remove generated files and backups
clean:
    rm -f *.o $(PROGS) *~