From 543545ca9c650ddcf396b2dd59e4a3d1f425c4d9 Mon Sep 17 00:00:00 2001 From: Anthony Calandra Date: Sat, 24 Jun 2017 13:14:26 -0400 Subject: [PATCH] Add sections on the std::make_X helper functions; improve section on smart pointers; structured bindings typo. --- README.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7ee484d..5f2790a 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ C++14 includes the following new language features: C++14 includes the following new library features: - [user-defined literals for standard library types](#user-defined-literals-for-standard-library-types) - [compile-time integer sequences](#compile-time-integer-sequences) +- [std::make_unique](#stdmake_unique) C++11 includes the following new language features: - [move semantics](#move-semantics) @@ -79,6 +80,7 @@ C++11 includes the following new library features: - [std::tie](#stdtie) - [std::array](#stdarray) - [unordered containers](#unordered-containers) +- [std::make_shared](#stdmake_shared) - [memory model](#memory-model) ## C++17 Language Features @@ -217,7 +219,7 @@ namespace A::B::C { ``` ### Structured bindings -A proposal for de-structuring initialization, that would allow writing `auto {x, y, z} = expr;` where the type of `expr` was a tuple-like object, whose elements would be bound to the variables `x`, `y`, and `z` (which this construct declares). _Tuple-like objects_ include [`std::tuple`](#tuples), `std::pair`, [`std::array`](#stdarray), and aggregate structures. +A proposal for de-structuring initialization, that would allow writing `auto [ x, y, z ] = expr;` where the type of `expr` was a tuple-like object, whose elements would be bound to the variables `x`, `y`, and `z` (which this construct declares). _Tuple-like objects_ include [`std::tuple`](#tuples), `std::pair`, [`std::array`](#stdarray), and aggregate structures. ```c++ using Coordinate = std::pair; Coordinate origin() { @@ -575,6 +577,21 @@ decltype(auto) a2t(const std::array& a) { } ``` +### std::make_unique +`std::make_unique` is the recommended way to create instances of `std::unique_ptr`s due to the following reasons: +* Avoid having to use the `new` operator. +* Prevents code repetition when specifying the underlying type the pointer shall hold. +* Most importantly, it provides exception-safety. Suppose we were calling a function `foo` like so: +```c++ +foo(std::unique_ptr{ new T{} }, function_that_throws(), std::unique_ptr{ new T{} }); +``` +The compiler is free to call `new T{}`, then `function_that_throws()`, and so on... Since we have allocated data on the heap in the first construction of a `T`, we have introduced a leak here. With `std::make_unique`, we are given exception-safety: +```c++ +foo(std::make_unique(), function_that_throws(), std::make_unique()); +``` + +See the section on [smart pointers](#smart-pointers) for more information on `std::unique_ptr` and `std::shared_ptr`. + ## C++11 Language Features ### Move semantics @@ -1130,13 +1147,15 @@ static_assert(std::is_same::type, int>::valu ``` ### Smart pointers -C++11 introduces new smart(er) pointers: `std::unique_ptr`, `std::shared_ptr`, `std::weak_ptr`. `std::auto_ptr` now becomes deprecated and then eventually removed in C++17. `std::unique_ptr` is a non-copyable, movable smart pointer that properly manages arrays and STL containers. +C++11 introduces new smart(er) pointers: `std::unique_ptr`, `std::shared_ptr`, `std::weak_ptr`. `std::auto_ptr` now becomes deprecated and then eventually removed in C++17. + +`std::unique_ptr` is a non-copyable, movable smart pointer that properly manages arrays and STL containers. **Note: Prefer using the `std::make_X` helper functions as opposed to using constructors. See the sections for [std::make_unique](#stdmake_unique) and [std::make_shared](#stdmake_shared).** ```c++ -std::unique_ptr p1(new Foo); // `p1` owns `Foo` +std::unique_ptr p1 { new Foo{} }; // `p1` owns `Foo` if (p1) p1->bar(); { - std::unique_ptr p2(std::move(p1)); // Now `p2` owns `Foo` + std::unique_ptr p2 { std::move(p1) }; // Now `p2` owns `Foo` f(*p2); p1 = std::move(p2); // Ownership returns to `p1` -- `p2` gets destroyed @@ -1146,6 +1165,27 @@ if (p1) p1->bar(); // `Foo` instance is destroyed when `p1` goes out of scope ``` +A `std::shared_ptr` is a smart pointer that manages a resource that is shared across multiple owners. A shared pointer holds a _control block_ which has a few components such as the managed object and a reference counter. All control block access is thread-safe, however, manipulating the managed object itself is *not* thread-safe. +```c++ +void foo(std::shared_ptr t) { + // Do something with `t`... +} + +void bar(std::shared_ptr t) { + // Do something with `t`... +} + +void baz(std::shared_ptr t) { + // Do something with `t`... +} + +std::shared_ptr p1 { new T{} }; +// Perhaps these take place in another threads? +foo(p1); +bar(p1); +baz(p1); +``` + ### std::chrono The chrono library contains a set of utility functions and types that deal with _durations_, _clocks_, and _time points_. One use case of this library is benchmarking code: ```c++ @@ -1196,6 +1236,22 @@ These containers maintain average constant-time complexity for search, insert, a * `unordered_map` * `unordered_multimap` +### std::make_shared +`std::make_shared` is the recommended way to create instances of `std::shared_ptr`s due to the following reasons: +* Avoid having to use the `new` operator. +* Prevents code repetition when specifying the underlying type the pointer shall hold. +* It provides exception-safety. Suppose we were calling a function `foo` like so: +```c++ +foo(std::shared_ptr{ new T{} }, function_that_throws(), std::shared_ptr{ new T{} }); +``` +The compiler is free to call `new T{}`, then `function_that_throws()`, and so on... Since we have allocated data on the heap in the first construction of a `T`, we have introduced a leak here. With `std::make_unique`, we are given exception-safety: +```c++ +foo(std::make_unique(), function_that_throws(), std::make_unique()); +``` +* Prevents having to do two allocations. When calling `std::shared_ptr{ new T{} }`, we have to allocate memory for `T`, then in the shared pointer we have to allocate memory for the control block within the pointer. + +See the section on [smart pointers](#smart-pointers) for more information on `std::unique_ptr` and `std::shared_ptr`. + ### Memory model C++11 introduces a memory model for C++, which means library support for threading and atomic operations. Some of these operations include (but aren't limited to) atomic loads/stores, compare-and-swap, atomic flags, promises, futures, locks, and condition variables.