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) *~