Olsen Smart Pointers CppCon22
Olsen Smart Pointers CppCon22
delete[] y;
}
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
Points to an object
Can be dereferenced
Owns memory
Assumes it is the only owner
Automatically destroys the object and deletes the
memory Move-only type
void calculate_more(HelperType&);
void calculate_more(HelperType&);
void calculate_more(HelperType&);
void calculate_more(HelperType&);
Delete happens
automatically
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-
> ...
};
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-
> ...
};
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-
> ...
};
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-
> ...
};
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-
> ...
};
Combines together:
• Allocates memory
• Wraps it in a std::unique_ptr<T>
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
is better written as
auto owner = std::make_unique<HelperType>(inputs);
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
make_unique is specialized
for array types
Argument is number of
elements, not constructor
arguments
WidgetBase* create_widget(InputType);
Owns memory
Shared ownership
Many std::shared_ptr objects work together to manage one object
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
object count =
contro 2
l
76 David Olsen – Back to Basics: Smart Pointers –
CppCon 2022
SHARED_PTR
API
Combines together:
• One memory allocation for both the object and the
control block
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();
Dangling references
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());
}
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());
}
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());
}
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());
}
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());
}
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();
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
• return this->shared_from_this();