189 lines
4.7 KiB
Markdown
189 lines
4.7 KiB
Markdown
<!-- .slide: data-background="#ccc" -->
|
|
|
|
# `std::shared_ptr<>`
|
|
|
|
___
|
|
|
|
### `std::shared_ptr<>`
|
|
|
|
* <!-- .element: class="fragment fade-in" --> one object == multiple owners
|
|
* <!-- .element: class="fragment fade-in" --> last referrer destroys the object
|
|
* <!-- .element: class="fragment fade-in" --> copying allowed
|
|
* <!-- .element: class="fragment fade-in" --> moving allowed
|
|
* <!-- .element: class="fragment fade-in" --> can use custom deleter
|
|
* <!-- .element: class="fragment fade-in" --> can use custom allocator
|
|
* <!-- .element: class="fragment fade-in" --> has a control block == impact on size of pointer end efficiency
|
|
|
|
<img data-src="img/sharedptr1inverted.png" alt="shared pointers" class="plain fragment fade-in">
|
|
|
|
___
|
|
<!-- .slide: style="font-size: 0.85em" -->
|
|
|
|
### `std::shared_ptr<>` usage (1)
|
|
|
|
* Copying and moving is allowed
|
|
|
|
<div class="multicolumn">
|
|
<div class="col">
|
|
|
|
```cpp
|
|
std::shared_ptr<MyData> source();
|
|
void sink(std::shared_ptr<MyData> ptr);
|
|
|
|
void simpleUsage() {
|
|
source();
|
|
sink(source());
|
|
auto ptr = source();
|
|
sink(ptr);
|
|
sink(std::move(ptr));
|
|
auto p1 = source();
|
|
auto p2 = p1;
|
|
p2 = std::move(p1);
|
|
p1 = p2;
|
|
p1 = std::move(p2);
|
|
}
|
|
|
|
```
|
|
|
|
</div>
|
|
|
|
<div class="col">
|
|
|
|
```cpp
|
|
std::shared_ptr<MyData> source();
|
|
void sink(std::shared_ptr<MyData> ptr);
|
|
|
|
void collections() {
|
|
std::vector<std::shared_ptr<MyData>> v;
|
|
|
|
v.push_back(source());
|
|
|
|
auto tmp = source();
|
|
v.push_back(tmp);
|
|
v.push_back(std::move(tmp));
|
|
|
|
sink(v[0]);
|
|
sink(std::move(v[0]));
|
|
}
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
___
|
|
<!-- .slide: style="font-size: 0.85em" -->
|
|
|
|
### `std::shared_ptr<>` usage (2)
|
|
|
|
```cpp
|
|
#include <memory>
|
|
#include <map>
|
|
#include <string>
|
|
|
|
class Gadget {};
|
|
std::map<std::string, std::shared_ptr<Gadget>> gadgets;
|
|
|
|
void foo() {
|
|
std::shared_ptr<Gadget> p1{new Gadget()}; // reference counter = 1
|
|
{
|
|
auto p2 = p1; // copy (reference counter == 2)
|
|
gadgets.insert(make_pair("mp3", p2)); // copy (reference counter == 3)
|
|
p2->use();
|
|
} // destruction of p2, reference counter = 2
|
|
} // destruction of p1, reference counter = 1
|
|
|
|
int main() {
|
|
foo();
|
|
gadgets.clear(); // reference counter = 0 - gadget is removed
|
|
}
|
|
```
|
|
___
|
|
|
|
### Custom deleter
|
|
|
|
* <!-- .element: class="fragment fade-in" --> Don't change a type of <code>shared_ptr</code> because data is stored in control block
|
|
* <!-- .element: class="fragment fade-in" --> Don't change a size of <code>shared_ptr</code> because data is stored in control block
|
|
* <!-- .element: class="fragment fade-in" --> You can have a collection of <code>shared_ptr</code> which has different deleter
|
|
|
|
<div class="multicolumn">
|
|
<div class="col">
|
|
|
|
```C++
|
|
class Foo {};
|
|
|
|
void deleter1(Foo* const foo) {
|
|
std::cout << "Deleter1\n";
|
|
delete foo;
|
|
}
|
|
|
|
int main() {
|
|
std::vector<std::shared_ptr<Foo>> vec;
|
|
std::shared_ptr<Foo> ptr1(new Foo(), deleter1);
|
|
vec.push_back(std::move(ptr1));
|
|
|
|
auto deleter2 = [](Foo* const foo) {
|
|
std::cout << "Deleter2\n";
|
|
delete foo;
|
|
};
|
|
vec.emplace_back(new Foo(), deleter2);
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
</div>
|
|
|
|
<div class="col">
|
|
|
|
```Bash
|
|
Deleter1
|
|
Deleter2
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
</div>
|
|
|
|
<!-- .slide: style="font-size: 0.8em" -->
|
|
___
|
|
|
|
### Problem with shared_ptr
|
|
|
|
Let's look at a short story:
|
|
|
|
* <!-- .element: class="fragment fade-in" --> Programmer1 : Why do you pass shared_ptr by copy? Passing by copy increment counter (slower then unique or raw ptr)
|
|
* <!-- .element: class="fragment fade-in" --> Programmer2: Ok, so I will pass it by const reference instead! And avoid unnecessary incrementation of the control block.
|
|
* <!-- .element: class="fragment fade-in" --> Programmer1: So if you don't need to copy it (don't need to have 2 owners) why don't use unique_ptr?
|
|
* <!-- .element: class="fragment fade-in" --> <b>Reassume</b>: shared_ptr should be use <b>only when given resource need to have few owners</b> (very rare situation). In other case use always unique_ptr!
|
|
|
|
___
|
|
|
|
### `std::shared_ptr<>` cyclic dependencies
|
|
|
|
* What happens here?
|
|
|
|
<div class="multicolumn" style="position: relative">
|
|
<div class="col" style="width: 65%; flex: none">
|
|
|
|
```cpp
|
|
#include <memory>
|
|
|
|
struct Node {
|
|
std::shared_ptr<Node> child;
|
|
std::shared_ptr<Node> parent;
|
|
};
|
|
|
|
int main () {
|
|
auto root = std::shared_ptr<Node>(new Node);
|
|
auto child = std::shared_ptr<Node>(new Node);
|
|
|
|
root->child = child;
|
|
child->parent = root;
|
|
}
|
|
|
|
|
|
```
|
|
|
|
</div>
|
|
|
|
<div class="col fragment fade-in">
|
|
Memory leak!
|
|
<img data-src="img/kot.jpg" alt="kot" class="plain" style="height: 50%">
|
|
|
|
</div>
|