0% found this document useful (0 votes)
9 views137 pages

Olsen Smart Pointers CppCon22

The document discusses the use of raw and smart pointers in C++, emphasizing the importance of memory management and ownership semantics. It covers the implementation and usage of unique pointers, including their move-only nature and automatic resource management. The content is derived from a presentation by David Olsen at CppCon 2022, focusing on best practices for using pointers in C++.

Uploaded by

ajantharajaht
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views137 pages

Olsen Smart Pointers CppCon22

The document discusses the use of raw and smart pointers in C++, emphasizing the importance of memory management and ownership semantics. It covers the implementation and usage of unique pointers, including their move-only nature and automatic resource management. The content is derived from a presentation by David Olsen at CppCon 2022, focusing on best practices for using pointers in C++.

Uploaded by

ajantharajaht
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 137

void science(double* data, int N)

{ double* temp = new double[N*2];


do_setup(data, temp, N);
if (not needed(data, temp, N))
return;
calculate(data, temp, N);
delete[] temp;
}
2 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
void science(double* data, int N)
{ double* temp = new double[N*2];
do_setup(data, temp, N);
if (not needed(data, temp, N))
return;
Early return skips
calculate(data, temp, N); delete
delete[] temp;
}
3 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
void science(double* x, int N) {
double* y = new double[N];
double* z = new double[N]; y,
calculate(x, z, N);
delete[] z;
delete[] y;
}

4 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
void science(double* x, int N) {
double* y = new double[N];
double* z = new double[N]; y,
calculate(x, z, N); If second new
throws,
delete[] z; y is leaked

delete[] y;
}

5 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
float* science(float* x, float* float* z y, int N) {
= new float[N]; saxpy(2.5, x, y, z,
N);
delete[] x;
delete[] y;
return z;
}

6 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
float* science(float* x, float* float* z y, int N) {
= new float[N]; saxpy(2.5, x, y, z,
N);
delete[] x;
Can x and y be
delete[] y; deleted?

return z; Caller is expected to


delete[] z
}

7 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
RAW
POINTER
Too many uses

Single object vs. array

Owning vs. non-


owning Nullable vs.
non-nullable

8 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
RAW POINTER
Too many uses

Single object vs. array

Single: allocate with new, free with delete

Array: allocate with new[], free with delete[]

Single: don’t use ++p, --p, or p[n]


Array: ++p, --p, and p[n] are fine

9 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
RAW POINTER
Too many uses

Single object vs. array


Owning vs. non-owning
Owner must free the memory when done

Non-owner must never free the memory

10 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
RAW POINTER
Too many uses

Single object vs. array

Owning vs. non-


owning Nullable vs.
non-nullable
Some pointers can
never be null

It would be nice if
the type system
helped enforce that
11 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
RAW POINTER
Too many uses

Single object vs. array

Owning vs. non-


owning Nullable vs.
non-nullable
The type system
doesn’t help
T* can be used for
all combinations of
those
12 characteristics David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
BACK TO
BASICS:
SMART
POINTERS
SMART
POINTER

Behaves like a pointer


… at least one of the roles of a pointer

Points to an object

Can be dereferenced

Adds additional “smarts”


Often limits behavior to certain of a pointer’s possible roles

14 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SMART
POINTER
What is it good for?

“Smart” can be almost anything


Automatically release resources is most

common Enforce restrictions, e.g. don’t

allow nullptr Extra safety checks

Sometimes the smarts are only in the


name
gsl::owner<T> is just a typedef of T*; it
only has meaning for those reading the
code
15 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
RAW
POINTER
What is it good for?

Non-owning pointer to a single object


Use a smart pointer for all owning pointers
Use a span type in place of non-owning pointers to arrays
C++20 std::span, or gsl::span

16 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PT
17
R
David Olsen – Back to Basics: Smart Pointers – CppCon 2022
UNIQUE_PTR
Overview

Owns memory
Assumes it is the only owner
Automatically destroys the object and deletes the
memory Move-only type

18 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Overview

Defined in header <memory>

One required template parameter, which is the pointed-to type


template <typename T>
struct unique_ptr {
// ...
using element_type = T;
using pointer = T*;
// ...
};

19 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Basic usage - function

void calculate_more(HelperType&);

ResultType do_work(InputType inputs) {


std::unique_ptr<HelperType> owner{new HelperType(inputs)};
owner->calculate();
calculate_more(*owner);
return owner->important_result();
}

20 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Basic usage - function

void calculate_more(HelperType&);

ResultType do_work(InputType inputs) {


std::unique_ptr<HelperType> owner{new HelperType(inputs)};
owner->calculate();
calculate_more(*owner);
return owner->important_result();
}

Create unique_ptr with newly allocated


memory

21 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Basic usage - function

void calculate_more(HelperType&);

ResultType do_work(InputType inputs) {


std::unique_ptr<HelperType> owner{new HelperType(inputs)};
owner->calculate();
calculate_more(*owner);
return owner->important_result();
}

Dereference the unique_ptr

22 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Basic usage - function

void calculate_more(HelperType&);

ResultType do_work(InputType inputs)


{ std::unique_ptr<HelperType> owner{new HelperType(inputs)};
owner->calculate();
calculate_more(*owner);
return owner->important_result();
}

Delete happens
automatically

23 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Basic usage - class

WidgetBase* create_widget(InputType);

class MyClass
{ std::unique_ptr<WidgetBase> owner;
public:
MyClass(InputType inputs)
: owner(create_widget(inputs)) { }
~MyClass() = default;
// ... member functions that use owner-
> ...
};

24 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Basic usage - class

WidgetBase* create_widget(InputType);

class MyClass
{ std::unique_ptr<WidgetBase> owner;
public:
MyClass(InputType inputs)
: owner(create_widget(inputs)) { }
~MyClass() = default;
// ... member functions that use owner-
> ...
};

25 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Basic usage - class

WidgetBase* create_widget(InputType);

class MyClass
{ std::unique_ptr<WidgetBase> owner;
public:
MyClass(InputType inputs)
: owner(create_widget(inputs)) { }
~MyClass() = default;
// ... member functions that use owner-
> ...
};

26 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Basic usage - class

WidgetBase* create_widget(InputType);

class MyClass
{ std::unique_ptr<WidgetBase> owner;
public:
MyClass(InputType inputs)
: owner(create_widget(inputs)) { }
~MyClass() = default;
// ... member functions that use owner-
> ...
};

27 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Basic usage - class

WidgetBase* create_widget(InputType);

class MyClass
{ std::unique_ptr<WidgetBase> owner;
public:
MyClass(InputType inputs)
: owner(create_widget(inputs)) { }
~MyClass() = default;
// ... member functions that use owner-
> ...
};

28 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
RAII

Very useful for implementing RAII


See “Back to Basics: RAII” by Andre Kostur, Tue 16:45-17:45, in
Summit 2 & 3

29 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Move-only

Move only type


No copy constructor or copy assignment operator

Unique ownership can’t be copied


“Back to Basics: Move Semantics”, David Olsen, CppCon 2020

30 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T>


class unique_ptr {
T* ptr;
public:
unique_ptr() noexcept : ptr(nullptr) { }
explicit unique_ptr(T* p) noexcept : ptr(p) { }
~unique_ptr() noexcept { delete ptr; }
// ...
};

31 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T>


class unique_ptr {
T* ptr;
public:
unique_ptr() noexcept : ptr(nullptr) { }
explicit unique_ptr(T* p) noexcept : ptr(p) { }
~unique_ptr() noexcept { delete ptr; }
// ...
};

32 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T> Points to the owned


class unique_ptr {
object
T* ptr;
public:
unique_ptr() noexcept : ptr(nullptr) { }
explicit unique_ptr(T* p) noexcept : ptr(p) { }
~unique_ptr() noexcept { delete ptr; }
// ...
};

33 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T>


class unique_ptr {
T* ptr;
public:
unique_ptr() noexcept : ptr(nullptr) { }
explicit unique_ptr(T* p) noexcept : ptr(p) { }
~unique_ptr() noexcept { delete ptr; }
// ...
};

34 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T>


class unique_ptr {
T* ptr;
public:
unique_ptr() noexcept : ptr(nullptr) { }
explicit unique_ptr(T* p) noexcept : ptr(p) { }
~unique_ptr() noexcept { delete ptr; }
// ...
};

35 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T>


class unique_ptr {
T* ptr;
public:
unique_ptr() noexcept : ptr(nullptr) { }
explicit unique_ptr(T* p) noexcept : ptr(p) { }
~unique_ptr() noexcept { delete ptr; }
// ...
};

36 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T> struct unique_ptr {


// ...
unique_ptr(unique_ptr const&) = delete;
unique_ptr(unique_ptr&& o) noexcept
: ptr(std::exchange(o.ptr, nullptr)) { }
unique_ptr& operator=(unique_ptr const&) =
unique_ptr& operator=(unique_ptr&&
delete; o) noexcept {
delete ptr;
ptr = o.ptr;
o.ptr = nullptr;
return *this;
}
// ...
37}; David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T> struct unique_ptr {


// ...
unique_ptr(unique_ptr const&) = delete;
unique_ptr(unique_ptr&& o) noexcept
: ptr(std::exchange(o.ptr, nullptr)) { }
unique_ptr& operator=(unique_ptr const&) =
unique_ptr& operator=(unique_ptr&&
delete; o) noexcept {
delete ptr;
ptr = o.ptr;
o.ptr = nullptr; Not
return *this; copyable
}
// ...
38}; David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T> struct unique_ptr {


// ...
unique_ptr(unique_ptr const&) = delete;
unique_ptr(unique_ptr&& o) noexcept
: ptr(std::exchange(o.ptr, nullptr)) { } unique_ptr&
operator=(unique_ptr const&) = delete; unique_ptr&
operator=(unique_ptr&& o) noexcept {
delete ptr;
ptr = o.ptr;
o.ptr = nullptr; Move constructor
return
transfers*this;
ownership
}
// ...
39}; David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T> struct unique_ptr {


// ...
unique_ptr(unique_ptr const&) = delete;
unique_ptr(unique_ptr&& o) noexcept
: ptr(std::exchange(o.ptr, nullptr)) { }
unique_ptr& operator=(unique_ptr const&) =
unique_ptr& operator=(unique_ptr&&
delete; o) noexcept {
delete ptr;
ptr = o.ptr;
o.ptr = nullptr; Frees
return *this;
} memory
// ...
};
40 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T> struct unique_ptr {


// ...
unique_ptr(unique_ptr const&) = delete;
unique_ptr(unique_ptr&& o) noexcept
: ptr(std::exchange(o.ptr, nullptr)) { } unique_ptr&
operator=(unique_ptr const&) = delete; unique_ptr&
operator=(unique_ptr&& o) noexcept {
delete ptr;
ptr = o.ptr;
o.ptr = nullptr; Transfers ownership
return *this;
}
// ...
41}; David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Sample
implementation

template <typename T>


struct unique_ptr {
// ...
T& operator*() const noexcept {
return *ptr;
}
T* operator->() const noexcept {
return ptr;
}
// ...
};

4 David Olsen – Back to Basics: Smart Pointers –


2 CppCon 2022
UNIQUE_PTR
Sample
implementation
template <typename T> struct unique_ptr {
T* release() noexcept {
T* old = ptr; ptr
= nullptr; return
old;
}
void reset(T* p = noexcept {
nullptr)
delete ptr;
ptr = p;
} T* get() const noexcept {
return ptr;
}
explicit operator bool() const noexcept { ptr !
return = nullptr;
}
43}; David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Sample
implementation
template <typename T> struct unique_ptr {
T* release() noexcept {
T* old = ptr; ptr
= nullptr; return Gives up
old; ownership
}
void reset(T* p = noexcept {
nullptr)
delete ptr;
ptr = p;
} T* get() const noexcept {
return ptr;
}
explicit operator bool() const noexcept { ptr !
return = nullptr;
}
44}; David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Sample
implementation
template <typename T> struct unique_ptr {
T* release() noexcept {
T* old = ptr; ptr
= nullptr; return
old;
}
void reset(T* p = noexcept {
nullptr) Cleans up
delete ptr; Takes
ptr = p; ownership
}
T* get() const
noexcept {
return
explicitptr;
operator bool() const noexcept {
} return ptr != nullptr;
}
45}; David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Sample
implementation
template <typename T> struct unique_ptr {
T* release() noexcept {
T* old = ptr; ptr
= nullptr; return
old;
}
void reset(T* p = noexcept {
nullptr)
delete ptr;
ptr = p;
}
T* get() const
noexcept {
return
explicitptr;
operator bool() const noexcept {
} return ptr != nullptr;
}
46}; David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Sample
implementation
template <typename T> struct unique_ptr {
T* release() noexcept {
T* old = ptr; ptr
= nullptr; return
old;
}
void reset(T* p = noexcept {
nullptr)
delete ptr;
ptr = p;
} T* get() const noexcept {
return ptr;
}
explicit operator bool() const noexcept {
return ptr != nullptr; Test for non-
} empty
47}; David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
MAKE_UNIQUE

template <typename T, typename... Args>


unique_ptr<T> make_unique(Args&&... args);

Combines together:
• Allocates memory

• Constructs a T with the given arguments

• Wraps it in a std::unique_ptr<T>

Prefer using make_unique to creating a unique_ptr


explicitly

48 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
MAKE_UNIQUE

template <typename T, typename... Args>


unique_ptr<T> make_unique(Args&&... args);

Combines
together: Can’t be
• Allocates memory deduced
Must be
• Constructs a T with the given
explicit
arguments

• Wraps it in a std::unique_ptr<T>
Prefer using make_unique to creating a unique_ptr
explicitly

49 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
MAKE_UNIQUE
Example

std::unique_ptr<HelperType> owner{new HelperType(inputs)};

is better written as
auto owner = std::make_unique<HelperType>(inputs);

50 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
MAKE_UNIQUE
Non-example

std::unique_ptr<WidgetBase> owner;
MyClass(InputType inputs)
: owner(create_widget(inputs)) { }
make_unique doesn’t help here
because allocation/construction happens within create_widget

51 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Array types

unique_ptr is specialized for array types


Calls delete[] instead of

delete Provides operator[]

make_unique is specialized
for array types
Argument is number of
elements, not constructor
arguments

52 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Array types

Fixing the first example from the beginning of the talk:


void science(double* data, int N) {
auto temp = std::make_unique<double[]>(N*2);
do_setup(data, temp.get(), N);
if (not needed(data, temp.get(), N))
return;
calculate(data, temp.get(), N);
}

53 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Array types

Fixing the first example from the beginning of the talk:


void science(double* data, int N) {
auto temp = std::make_unique<double[]>(N*2);
do_setup(data, temp.get(), N);
if (not needed(data, temp.get(), N))
return;
calculate(data, temp.get(), N);
}

54 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Array types

Fixing the first example from the beginning of the talk:


void science(double* data, int N) {
auto temp = std::make_unique<double[]>(N*2);
do_setup(data, temp.get(), N);
if (not needed(data, temp.get(), N))
return;
calculate(data, temp.get(), N);
}
std::unique_ptr<double[]>

55 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Array types

Fixing the first example from the beginning of the talk:


void science(double* data, int N) {
auto temp = std::make_unique<double[]>(N*2);
do_setup(data, temp.get(), N);
if (not needed(data, temp.get(), N))
return;
calculate(data, temp.get(), N);
}

unique_ptr destructor calls


delete[]
56 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Transfer ownership

Use move constructor/assignment to transfer


ownership
auto a = std::make_unique<T>();
// ...
std::unique_ptr<T> b{ a.release() };
// ...
a.reset(b.release());
Don’t do
that!
57 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Transfer ownership

Use move constructor/assignment to transfer ownership


auto a = std::make_unique<T>();
// ...
std::unique_ptr<T> b{ std::move(a) };
// ...
a = std::move(b);

Let unique_ptr handle the


details

58 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Transfer ownership

To transfer ownership to a function, pass std::unique_ptr by


value
To return ownership from a function, return std::unique_ptr by
value

59 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Transfer ownership

To transfer ownership to a function, pass std::unique_ptr by


value
To return ownership from a function, return std::unique_ptr by
value
float* science(
float* x,
float* y, int N)
{ float* z = new float[N];
saxpy(2.5, x, y, z, N);
delete[] x;
delete[] y;
60
return z; David Olsen – Back to Basics: Smart Pointers –
} CppCon 2022
UNIQUE_PTR
Transfer ownership

To transfer ownership to a function, pass std::unique_ptr by


value
To return ownership from a function, return std::unique_ptr by
value
std::unique_ptr<float[]>
science( std::unique_ptr<float[]> x,
std::unique_ptr<float[]> y, int N) {
auto z = std::make_unique<float[]>(N);
saxpy(2.5, x.get(), y.get(), z.get(), N);
}
return z;
61 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Transfer ownership

To transfer ownership to a function, pass std::unique_ptr by


value
To return ownership from a function, return std::unique_ptr by
value
std::unique_ptr<float[]>
science( std::unique_ptr<float[]> x,
std::unique_ptr<float[]> y, int N) {
auto z = std::make_unique<float[]>(N);
saxpy(2.5, x.get(), y.get(), z.get(), N);
return z; Arguments are now
} unique_ptr
62 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Transfer ownership

To transfer ownership to a function, pass std::unique_ptr by


value
To return ownership from a function, return std::unique_ptr by
value
std::unique_ptr<float[]>
science( std::unique_ptr<float[]> x,
std::unique_ptr<float[]> y, int N) {
auto z = std::make_unique<float[]>(N);
saxpy(2.5, x.get(), y.get(), z.get(), N);
return z; Return type is also
} unique_ptr
6 David Olsen – Back to Basics: Smart Pointers –
3 CppCon 2022
UNIQUE_PTR
Transfer ownership

To transfer ownership to a function, pass std::unique_ptr by


value
To return ownership from a function, return std::unique_ptr by
value
std::unique_ptr<float[]>
science( std::unique_ptr<float[]> x,
std::unique_ptr<float[]> y, int N) {
auto z = std::make_unique<float[]>(N);
saxpy(2.5, x.get(), y.get(), z.get(), No
N); need to delete
return z; unique_ptr does
} that
64 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Transfer ownership

WidgetBase* create_widget(InputType);

better communicates its intent if changed to


std::unique_ptr<WidgetBase> create_widget(InputType);

65 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Gotchas

Make sure only one unique_ptr for a block of memory


T* p = ...;
std::unique_ptr<T> a{p};
std::unique_ptr<T> b{p};
// crash due to double free
auto c = std::make_unique<T>();
std::unique_ptr<T> d{c.get()};
// crash due to double free
Don’t create a unique_ptr from a pointer unless you know where the pointer
came
from and that it needs an owner
66 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
UNIQUE_PTR
Gotchas

unique_ptr doesn’t solve the dangling pointer problem


T* p = nullptr;
{
auto u = std::make_unique<T>();
p = u.get();
}
// p is now dangling and invalid
auto bad = *p; // undefined behavior

67 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR
Collection

std::vector<std::unique_ptr<T>> just works


{
std::vector<std::unique_ptr<T>> v;
v.push_back(std::make_unique<T>());
std::unique_ptr<T> a;
v.push_back(std::move(a));
v[0] = std::make_unique<T>();
auto it = v.begin(); v.erase(it);
}

68 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_P
69
TR
David Olsen – Back to Basics: Smart Pointers – CppCon 2022
SHARED_PTR
Overview

Owns memory
Shared ownership
Many std::shared_ptr objects work together to manage one object

Automatically destroys the object and deletes the memory


Copyable

70 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
Overview

Defined in header <memory>

One required template parameter, which is the pointed-


to type
template <typename T>
// ... shared_ptr {
struct
using element_type = T;
// ...
};

71 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED
OWNERSHIP

Ownership is shared equally


No way to force a shared_ptr to give up its ownership

Cleanup happens when the last shared_ptr gives up ownership

72 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED
OWNERSHIP
Examples

Real world
Community

garden Open

source project

Shared
responsibility for
maintenance

They survive as
long as one
person is willing
73 David Olsen – Back to Basics: Smart Pointers –
to do the work CppCon 2022
SHARED
OWNERSHIP
Examples

In code
UI widgets

Promise/futur

Often
implemented
with
reference
counting or
garbage
74 collection David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SHARED_PTR
Reference counting

Shared ownership implemented with reference


counting
Control block on the heap for bookkeeping T
object
contro
l object
count =
1

75 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
Reference counting

Shared ownership implemented with reference


counting
Control block on the heap for bookkeeping T
object
contro
l object

object count =

contro 2
l
76 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
shared_ptr() noexcept;
explicit shared_ptr(T*);
~shared_ptr() noexcept;
// ...
};

77 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
shared_ptr() noexcept; Creates empty
explicit shared_ptr(T*); shared_ptr
~shared_ptr() noexcept;
// ...
};

78 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
shared_ptr() noexcept;
explicit shared_ptr(T*); Starts managing an
~shared_ptr() noexcept; object
// ...
};

79 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
shared_ptr() noexcept;
explicit shared_ptr(T*); Decrements count
~shared_ptr() noexcept;
Cleanup if count
// ...
== 0
};

80 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
shared_ptr(shared_ptr const&) noexcept;
shared_ptr(shared_ptr&&) noexcept;
shared_ptr(unique_ptr<T>&&);

shared_ptr& operator=(shared_ptr const&) noexcept;


shared_ptr& operator=(shared_ptr&&) noexcept;
shared_ptr& operator=(unique_ptr<T>&&);
// ...
};

81 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

Copies object and control block


template <typename T>
struct shared_ptr { pointers
// ... Increments count
shared_ptr(shared_ptr const&) noexcept;
shared_ptr(shared_ptr&&) noexcept;
shared_ptr(unique_ptr<T>&&);

shared_ptr& operator=(shared_ptr const&) noexcept;


shared_ptr& operator=(shared_ptr&&) noexcept;
shared_ptr& operator=(unique_ptr<T>&&);
// ...
};

82 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
shared_ptr(shared_ptr const&) noexcept;
shared_ptr(shared_ptr&&) noexcept;
shared_ptr(unique_ptr<T>&&); Transfers
ownership
shared_ptr& operator=(shared_ptr const&) noexcept;
shared_ptr& operator=(shared_ptr&&) noexcept;
shared_ptr& operator=(unique_ptr<T>&&);
// ...
};

83 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
shared_ptr(shared_ptr const&) noexcept;
shared_ptr(shared_ptr&&) noexcept;
shared_ptr(unique_ptr<T>&&); Transfers
ownership
shared_ptr& operator=(shared_ptr const&) noexcept;
shared_ptr& operator=(shared_ptr&&) noexcept;
shared_ptr& operator=(unique_ptr<T>&&);
// ...
};

84 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
shared_ptr(shared_ptr const&) noexcept;
shared_ptr(shared_ptr&&) noexcept;
shared_ptr(unique_ptr<T>&&);

shared_ptr& operator=(shared_ptr const&) noexcept;


shared_ptr& operator=(shared_ptr&&) noexcept;
shared_ptr& operator=(unique_ptr<T>&&);
// ...
};

85 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
T& operator*() const noexcept;
T* operator->() const noexcept;
// ...
};

86 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
void reset(T*);
T* get() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
};

87 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
void reset(T*);
T* get() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
};

88 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
void reset(T*);
T* get() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
};

89 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
void reset(T*);
T* get() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
};

90 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
API

template <typename T>


struct shared_ptr {
// ...
void reset(T*);
T* get() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
};

91 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
MAKE_SHARED

template <typename T, typename... Args>


shared_ptr<T> make_shared(Args&&... args);

Combines together:
• One memory allocation for both the object and the
control block

• Constructs a T with the given arguments

• Initializes the control block

• Wraps them in a std::shared_ptr<T> object

Prefer using make_shared to creating a shared_ptr directly


92 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SHARED_PTR
Shared ownership

To share ownership, additional shared_ptr objects must be created or


assigned from
an existing shared_ptr, not from the raw pointer
{
T* p = ...;
std::shared_ptr<T> a(p);
std::shared_ptr<T> b(p);
} // runtime error: double free

93 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
Shared ownership

To share ownership, additional shared_ptr objects must be created or


assigned from
an existing shared_ptr, not from the raw pointer
{
auto a = std::make_shared<T>();
std::shared_ptr<T> b(a.get());
} // runtime error: double free

94 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
Shared ownership

To share ownership, additional shared_ptr objects must be created or


assigned from
an existing shared_ptr, not from the raw pointer
{
auto a = std::make_shared<T>();
std::shared_ptr<T> b(a);
std::shared_ptr<T> c;
c = b;
}

95 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
Thread safety

Updating the same control block from different threads is


thread safe
auto a = std::make_shared<int>(42);
std::shared_ptr<int>
std::thread c = b;
t([](std::shared_ptr<int> b) {
work(*c);
}, a);
{
std::shared_ptr<int> d = a;
a.reset((int*)nullptr);
more_work(*d);
}
t.join();
96 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SHARED_PTR
Thread safety

Updating the same control block from different threads is


thread safe
auto a = std::make_shared<int>(42);
std::shared_ptr<int>
std::thread c = b;
t([](std::shared_ptr<int> b) { Increment
work(*c); count
}, a);
{
std::shared_ptr<int> d = a;
a.reset((int*)nullptr); Decrement
more_work(*d); count
}
t.join();
97 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SHARED_PTR
Thread safety

Updating the same control block from different threads is


thread safe
auto a = std::make_shared<int>(42);
std::shared_ptr<int>
std::thread c = b;
t([](std::shared_ptr<int> b) {
work(*c);
}, a);
{ Read
std::shared_ptr<int> d = a; object
a.reset((int*)nullptr);
more_work(*d);
}
t.join();
98 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SHARED_PTR
Thread safety

Updating the managed object from different threads is not


thread safe
auto a = std::make_shared<int>(42);
std::thread t([](std::shared_ptr<int> b) {
std::shared_ptr<int> c = b;
}, *c
a);= 100;
{
std::shared_ptr<int> d = a;
a.reset((int*)nullptr);
*d = 200;
}
t.join();
9 David Olsen – Back to Basics: Smart Pointers –
9 CppCon 2022
SHARED_PTR
Thread safety

Updating the managed object from different threads is not


thread safe
auto a = std::make_shared<int>(42);
std::thread t([](std::shared_ptr<int> b) {
std::shared_ptr<int> c = b;
}, *c
a);= 100;
{
Write to
std::shared_ptr<int> d = a;
object
a.reset((int*)nullptr);
*d = 200; Data race!
}
t.join();
100 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SHARED_PTR
Thread safety

Updating the same shared_ptr object from different threads is not thread
safe
auto a = std::make_shared<int>(42);
std::thread t([&]() {
work(*a);
});
a = std::make_shared<int>(100);
t.join();

101 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
Thread safety

Updating the same shared_ptr object from different threads is not


thread safe
auto a = std::make_shared<int>(42);
work(*a);t([&]() {
std::thread Capture ‘a’ by
}); reference
a = std::make_shared<int>(100);
t.join();

102 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
Thread safety

Updating the same shared_ptr object from different threads is not


thread safe
auto a = std::make_shared<int>(42);
work(*a);t([&]() {
std::thread Read and
}); write
a = std::make_shared<int>(100); Data race!
t.join();

103 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
SHARED_PTR
Arrays

shared_ptr added support for array types in


C++17

make_shared added support for array types in


C++20 Use array types with shared_ptr with
caution
Make sure your standard library is new enough

104 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
UNIQUE_PTR VS
SHARED_PTR

Single owner: use unique_ptr


Multiple owners: use shared_ptr
Non-owning reference: use something else
entirely When in doubt, prefer unique_ptr
Easier to switch from unique_ptr to
shared_ptr than the other way around

105 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
ADVANCED
106 STUFF
David Olsen – Back to Basics: Smart Pointers – CppCon 2022
WEAK_PT
107
R
David Olsen – Back to Basics: Smart Pointers – CppCon 2022
WEAK_PTR
Overview

A non-owning reference to a shared_ptr-managed object


Knows when the lifetime of the managed object ends
std::weak_ptr<int> w;
{
auto s = std::make_shared<int>(42);
w = s;
std::shared_ptr<int> t = w.lock();
if (t) printf("%d\n", *t);
}
std::shared_ptr<int> u = w.lock();
if (!u) printf("empty\n");
108 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
WEAK_PTR
Overview

A non-owning reference to a shared_ptr-managed object


Knows when the lifetime of the managed object ends
std::weak_ptr<int> w;
{
auto s = std::make_shared<int>(42);
w = s;
std::shared_ptr<int> t = w.lock();
if (t) printf("%d\n", *t);
}
std::shared_ptr<int> u = w.lock();
if (!u) printf("empty\n");
109 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
WEAK_PTR
Overview

A non-owning reference to a shared_ptr-managed object


Knows when the lifetime of the managed object ends
std::weak_ptr<int> w;
{
auto s = std::make_shared<int>(42);
w = s;
std::shared_ptr<int> t = w.lock();
if (t) printf("%d\n", *t);
}
std::shared_ptr<int> u = w.lock();
if (!u) printf("empty\n");
110 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
WEAK_PTR
Overview

A non-owning reference to a shared_ptr-managed object


Knows when the lifetime of the managed object ends
std::weak_ptr<int> w;
{
auto s = std::make_shared<int>(42);
w = s;
std::shared_ptr<int> t = w.lock();
if (t) printf("%d\n", *t);
}
std::shared_ptr<int> u = w.lock();
if (!u) printf("empty\n");
111 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
WEAK_PTR
Overview

A non-owning reference to a shared_ptr-managed object


Knows when the lifetime of the managed object ends
std::weak_ptr<int> w;
{
auto s = std::make_shared<int>(42);
w = s;
std::shared_ptr<int> t = w.lock();
if (t) printf("%d\n", *t);
}
std::shared_ptr<int> u = w.lock();
if (!u) printf("empty\n");
112 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
WEAK_PTR
Overview

A non-owning reference to a shared_ptr-managed object


Knows when the lifetime of the managed object ends
std::weak_ptr<int> w;
{
auto s = std::make_shared<int>(42);
w = s;
std::shared_ptr<int> t = w.lock();
if (t) printf("%d\n", *t);
}
std::shared_ptr<int> u = w.lock();
if (!u) printf("empty\n");
113 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
WEAK_PTR
Overview

A non-owning reference to a shared_ptr-managed object


Knows when the lifetime of the managed object ends
std::weak_ptr<int> w;
{
auto s = std::make_shared<int>(42);
w = s;
std::shared_ptr<int> t = w.lock();
if (t) printf("%d\n", *t);
}
std::shared_ptr<int> u = w.lock();
if (!u) printf("empty\n");
114 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
WEAK_PTR
What is it good for?

Only useful when object is managed by shared_ptr


Caching
Keep a reference to an object for faster access

Don’t want that reference to keep the object alive

Dangling references

115 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
116 DELETERS
David Olsen – Back to Basics: Smart Pointers – CppCon 2022
CUSTOM
DELETERS

What if cleanup action is something other than calling


delete ?
FILE* fp = fopen("readme.txt", "r");
fread(buffer, 1, N, fp); fclose(fp);

117 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS

What if cleanup action is something other than calling


delete ?
FILE* fp = fopen("readme.txt", "r");
fread(buffer, 1, N, fp); fclose(fp);

Might be forgotten or skipped

118 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
unique_ptr

unique_ptr has an extra defaulted template parameter for the


delete
template <typename T,
typename Deleter = std::default_delete<T>>
class unique_ptr;
Type Deleter must have an operator()(T*)

make_unique doesn’t support custom deleters


unique_ptr with custom deleter must be constructed
directly

119 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
unique_ptr

struct fclose_deleter {
void operator()(FILE* fp) const { fclose(fp); }
};
using unique_FILE = std::unique_ptr<FILE, fclose_deleter>;
{
unique_FILE fp(fopen("readme.txt", "r"));
fread(buffer, 1, N, fp.get());
}

120 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
unique_ptr

struct fclose_deleter {
void operator()(FILE* fp) const { fclose(fp); }
};
using unique_FILE = std::unique_ptr<FILE, fclose_deleter>;
{
unique_FILE fp(fopen("readme.txt", "r"));
fread(buffer, 1, N, fp.get());
}

121 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
unique_ptr

struct fclose_deleter {
void operator()(FILE* fp) const { fclose(fp); }
};
using unique_FILE = std::unique_ptr<FILE, fclose_deleter>;
{
unique_FILE fp(fopen("readme.txt", "r"));
fread(buffer, 1, N, fp.get());
}

122 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
unique_ptr

struct fclose_deleter {
void operator()(FILE* fp) const { fclose(fp); }
};
using unique_FILE = std::unique_ptr<FILE, fclose_deleter>;
{
unique_FILE fp(fopen("readme.txt", "r"));
fread(buffer, 1, N, fp.get());
}

123 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
unique_ptr

struct fclose_deleter {
void operator()(FILE* fp) const { fclose(fp); }
};
using unique_FILE = std::unique_ptr<FILE, fclose_deleter>;
{
unique_FILE fp(fopen("readme.txt", "r"));
fread(buffer, 1, N, fp.get());
}

fclose called automatically

124 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
shared_ptr

Custom deleter for shared_ptr is passed to constructor, where it is


type erased
struct fclose_deleter {
void operator()(FILE* fp) const { fclose(fp); }
};
{
std::shared_ptr<FILE> fp(fopen("readme.txt", "r"),
fclose_deleter{});
fread(buffer, 1, N, fp.get()); std::shared_ptr<FILE>
fp2(fp);
}

125 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
shared_ptr

Custom deleter for shared_ptr is passed to constructor, where it is


type erased
struct fclose_deleter {
void operator()(FILE* fp) const { fclose(fp); }
};
{
std::shared_ptr<FILE> fp(fopen("readme.txt", "r"),
fclose_deleter{});
fread(buffer, 1, N, fp.get()); std::shared_ptr<FILE>
fp2(fp);
}

126 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
shared_ptr

Custom deleter for shared_ptr is passed to constructor, where it is


type erased
struct fclose_deleter {
void operator()(FILE* fp) const { fclose(fp); }
};
{
std::shared_ptr<FILE> fp(fopen("readme.txt", "r"),
fclose_deleter{});
fread(buffer, 1, N, fp.get()); std::shared_ptr<FILE>
fp2(fp);
}

127 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CUSTOM
DELETERS
shared_ptr

Custom deleter for shared_ptr is passed to constructor, where it is


type erased
struct fclose_deleter {
void operator()(FILE* fp) const { fclose(fp); }
};
{
std::shared_ptr<FILE> fp(fopen("readme.txt", "r"),
fclose_deleter{});
fread(buffer, 1, N, fp.get());
std::shared_ptr<FILE> fp2(fp);
}
fclose called
automatically
128 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SMART_PTR
129 EXTRAS
David Olsen – Back to Basics: Smart Pointers – CppCon 2022
CASTS

To have share_ptrs of different types that manage the same object


dynamic_pointer_cast, static_pointer_cast, const_pointer_cast,
reinterpret_pointer_cast

std::shared_ptr<WidgetBase> p = create_widget(inputs);
std::shared_ptr<BlueWidget> b =
std::dynamic_pointer_cast<BlueWidget>(p);
if (b)
b->do_something_blue();

130 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
ALIASING
CONSTRUCTOR

Two shared_ptrs use same control block, but have unrelated object pointers
Useful for pointers to subobjects of managed objects
struct Outer {
int a;
Inner inner;
};
void f(std::shared_ptr<Outer> op) { std::shared_ptr<Inner>
ip(op, &op->inner);
// ...
}
131 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SHARED_FROM_T
HIS

To convert this into a shared_ptr


• Class derives from enable_shared_from_this

• Object is already managed by a shared_ptr

• return this->shared_from_this();

132 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
CONCLUSI
133 ONS
David Olsen – Back to Basics: Smart Pointers – CppCon 2022
RAW POINTERS VS SMART
POINTERS

Raw pointers can fulfill lots of roles


Can’t fully communicate the programmer’s
intent

Smart pointers can be very powerful


Automatic tasks, especially

cleanup Extra checking

Limited API, to better express


programmer’s intent

134 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
STANDARD VS CUSTOM
SMART POINTERS

Standard C++ has two commonly used smart pointers


unique_ptr and shared_ptr

Use them whenever they fit your needs

Don’t limit yourself to standard smart pointers


If your framework has smart pointers, use

them Write your own if necessary

“The Smart Pointers I Wish I Had,”


Matthew Fleming, CppCon 2019

135 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022
GUIDELINES

Use smart pointers to represent ownership

Prefer unique_ptr over


shared_ptr Use make_unique
and make_shared
Pass/return unique_ptr to
transfer ownership between
functions

136 David Olsen – Back to Basics: Smart Pointers –


CppCon 2022

You might also like