trainings/AdvancedCppV2/Presentation/smart_pointers_best_practices.md

196 lines
No EOL
7.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- .slide: data-background="#ccc" -->
# Best practices
___
## Best practices
* <!-- .element: class="fragment fade-in" --> Rule of 0, Rule of 5
* <!-- .element: class="fragment fade-in" --> Avoid explicit <code>new</code>
* <!-- .element: class="fragment fade-in" --> Use <code>std::make_shared()</code> / <code>std::make_unique()</code>
* <!-- .element: class="fragment fade-in" --> Avoid copying <code>std::shared_ptr<></code> when this is not neccessary.
* <!-- .element: class="fragment fade-in" --> Use references instead of pointers (as argument to function)
* <!-- .element: class="fragment fade-in" --> Almost always use <code>unique_ptr</code>
* <!-- .element: class="fragment fade-in" --> Use <code>shared_ptr</code> <b>ONLY</b> when you need to share ownership of object.
___
## Rule of 0, Rule of 5
### Rule of 5 <!-- .element: class="fragment fade-in" -->
* <!-- .element: class="fragment fade-in" --> If you need to implement one of those functions:
* <!-- .element: class="fragment fade-in" --> destructor
* <!-- .element: class="fragment fade-in" --> copy constructor
* <!-- .element: class="fragment fade-in" --> copy assignment operator
* <!-- .element: class="fragment fade-in" --> move constructor
* <!-- .element: class="fragment fade-in" --> move assignment operator
* <!-- .element: class="fragment fade-in" --> It probably means that you should implement them all, because you have manual resources management.
### Rule of 0 <!-- .element: class="fragment fade-in" -->
* <!-- .element: class="fragment fade-in" --> If you use RAII wrappers on resources, you dont need to implement any of Rule of 5 functions.
___
## Avoid explicit `new`
* <!-- .element: class="fragment fade-in" --> Smart pointers eliminate the need to use <code>delete</code> explicitly
* <!-- .element: class="fragment fade-in" --> To be symmetrical, do not use <code>new</code> as well
* <!-- .element: class="fragment fade-in" --> Allocate using:
* <!-- .element: class="fragment fade-in" --> <code>std::make_unique()</code>
* <!-- .element: class="fragment fade-in" --> <code>std::make_shared()</code>
* <!-- .element: class="fragment fade-in" --> use <code>new</code> only when you need to create ptr with custom deleter
___
<!-- .slide: style="font-size: 0.8em" -->
### Use `std::make_shared()` / `std::make_unique()`
* <!-- .element: class="fragment fade-in" --> What is a problem here?
```cpp
struct MyData { int value; };
using Ptr = std::shared_ptr<MyData>;
void sink(Ptr oldData, Ptr newData);
void use(void) {
sink(Ptr{new MyData{41}}, Ptr{new MyData{42}});
}
```
<!-- .element: class="fragment fade-in" -->
* <!-- .element: class="fragment fade-in" --> Hint: this version is not problematic
```cpp
struct MyData { int value; };
using Ptr = std::shared_ptr<MyData>;
void sink(Ptr oldData, Ptr newData);
void use(void) {
Ptr oldData{new MyData{41}};
Ptr newData{new MyData{42}};
sink(std::move(oldData), std::move(newData));
}
```
<!-- .element: class="fragment fade-in" -->
___
### Allocation deconstructed
`auto p = new MyData(10);` means:
* <!-- .element: class="fragment fade-in" --> allocate <code>sizeof(MyData)</code> bytes
* <!-- .element: class="fragment fade-in" --> run <code>MyData</code> constructor
* <!-- .element: class="fragment fade-in" --> assign address of allocated memory to <code>p</code>
Order of evaluation of any part of any expression, including order of evaluation of function arguments is <a href="https://en.cppreference.com/w/cpp/language/eval_order" target="_top">**unspecified**</a>. The compiler can evaluate operands and other subexpressions in any order, and may choose another order when the same expression is evaluated again. There is no concept of left-to-right or right-to-left evaluation in C++. <b>This is not a problem since C++17</b> due to the changes in the evaluation order of function arguments. Specifically, each argument to a function is required to fully execute before evaluation of other arguments.
<!-- .element: class="fragment fade-in box" -->
___
<!-- .slide: style="font-size: 0.77em" -->
### Unspecified order of evaluation
* How about two such operations (before C++17)?
| first operation (A) | second operation (B) |
| :-------------------------------------------- | :-------------------------------------------- |
| (1) allocate `sizeof(MyData)` bytes | (1) allocate `sizeof(MyData)` bytes |
| (2) run `MyData` constructor | (2) run `MyData` constructor |
| (3) assign address of allocated memory to `p` | (3) assign address of allocated memory to `p` |
* <!-- .element: class="fragment fade-in" --> Unspecified order of evaluation means that order can be for example:
* A1, A2, B1, B2, A3, B3
* <!-- .element: class="fragment fade-in" --> What if B2 throws an exception?
___
### Use `std::make_shared()` / `std::make_unique()`
* <!-- .element: class="fragment fade-in" --> <code>std::make_shared()</code> / <code>std::make_unique()</code> resolves this problem
```cpp
struct MyData{ int value; };
using Ptr = std::shared_ptr<MyData>;
void sink(Ptr oldData, Ptr newData);
void use() {
sink(std::make_shared<MyData>(41), std::make_shared<MyData>(42));
}
```
<!-- .element: class="fragment fade-in" -->
* <!-- .element: class="fragment fade-in" --> Fixes previous bug
* <!-- .element: class="fragment fade-in" --> Does not repeat a constructed type
* <!-- .element: class="fragment fade-in" --> Does not use explicit <code>new</code>
* <!-- .element: class="fragment fade-in" --> Optimizes memory usage (only for <code>std::make_shared()</code>)
___
## Copying `std::shared_ptr<>`
```cpp
void foo(std::shared_ptr<MyData> p);
void bar(std::shared_ptr<MyData> p) {
foo(p);
}
```
* <!-- .element: class="fragment fade-in" --> requires counters incrementing / decrementing
* <!-- .element: class="fragment fade-in" --> atomics / locks are not free
* <!-- .element: class="fragment fade-in" --> will call destructors
##### Can be better?
<!-- .element: class="fragment fade-in" -->
___
## Copying `std::shared_ptr<>`
```cpp
void foo(const std::shared_ptr<MyData> & p);
void bar(const std::shared_ptr<MyData> & p) {
foo(p);
}
```
* <!-- .element: class="fragment fade-in" --> as fast as pointer passing
* <!-- .element: class="fragment fade-in" --> no extra operations
* <!-- .element: class="fragment fade-in" --> not safe in multithreaded applications
___
### Use references instead of pointers
* <!-- .element: class="fragment fade-in" --> What is the difference between a pointer and a reference?
* <!-- .element: class="fragment fade-in" --> reference cannot be empty
* <!-- .element: class="fragment fade-in" --> reference, once assigned cannot point to anything else
* <!-- .element: class="fragment fade-in" --> Priorities of usage (if possible):
* <!-- .element: class="fragment fade-in" --> <code>(const) T&</code>
* <!-- .element: class="fragment fade-in" --> <code>std::unique_ptr&ltT&gt</code>
* <!-- .element: class="fragment fade-in" --> <code>std::shared_ptr&ltT&gt</code>
* <!-- .element: class="fragment fade-in" --> <code>T*</code>
___
## Exercise: List
Take a look at `List.cpp` file, where simple (and buggy) single-linked list is implemented.
* `pushFront` method adds a new `Node` at the begining of the list.
* `findByValue` method iterates over the list and returns the first Node with matching `value` or `nullptr`.
1. <!-- .element: class="fragment fade-in" --> Compile and run List application
2. <!-- .element: class="fragment fade-in" --> Fix memory leaks without introducing smart pointers
3. <!-- .element: class="fragment fade-in" --> Fix memory leaks with smart pointers. What kind of pointers needs to be applied and why?
4. <!-- .element: class="fragment fade-in" --> Add function to add a node at the end of the list (try to do this with time complexity O(1))
5. <!-- .element: class="fragment fade-in" --> Add function to delete node with provided value.
<!-- .slide: style="font-size: 0.8em" -->