213 lines
4.8 KiB
Markdown
213 lines
4.8 KiB
Markdown
<!-- .slide: data-background="#ccc" -->
|
|
|
|
## Cyclic dependencies
|
|
|
|
<img data-src="img/cyclicinverted.png" alt="cyclic dependencies" class="plain fragment fade-in">
|
|
|
|
* <!-- .element: class="fragment fade-in" --> Cyclic dependency is where you have class A with self-referencing member.
|
|
* <!-- .element: class="fragment fade-in" --> Cyclic dependency is where you have two classes A and B where A has a reference to B which has a reference to A.
|
|
* <!-- .element: class="fragment fade-in" --> How to fix it?
|
|
|
|
___
|
|
|
|
### `std::weak_ptr<>` to the rescue
|
|
|
|
* <!-- .element: class="fragment fade-in" --> does not own an object
|
|
* <!-- .element: class="fragment fade-in" --> observes only
|
|
* <!-- .element: class="fragment fade-in" --> must be converted to <code>std::shared_ptr<></code> to access the object
|
|
* <!-- .element: class="fragment fade-in" --> can be created only from a <code>std::shared_ptr<></code>
|
|
|
|
<div>
|
|
<img data-src="img/weakptrinverted.png" alt="weak pointers" class="plain fragment fade-in">
|
|
</div>
|
|
|
|
___
|
|
|
|
### `std::weak_ptr<>` usage
|
|
|
|
<div class="multicolumn" style="position: relative">
|
|
<div class="col" style="width: 65%; flex: none">
|
|
|
|
```cpp
|
|
#include <memory>
|
|
#include <iostream>
|
|
|
|
struct Msg { int value; };
|
|
|
|
void checkMe(const std::weak_ptr<Msg> & wp) {
|
|
std::shared_ptr<Msg> p = wp.lock();
|
|
if (p)
|
|
std::cout << p->value << '\n';
|
|
else
|
|
std::cout << "Expired\n";
|
|
}
|
|
|
|
int main() {
|
|
auto sp = std::shared_ptr<Msg>{new Msg{10}};
|
|
auto wp = std::weak_ptr<Msg>{sp};
|
|
checkMe(wp);
|
|
sp.reset();
|
|
checkMe(wp);
|
|
}
|
|
```
|
|
|
|
</div>
|
|
|
|
<div class="col">
|
|
|
|
```bash
|
|
> ./a.out
|
|
10
|
|
Expired
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
___
|
|
|
|
### `std::shared_ptr<>` cyclic dependencies
|
|
|
|
* How to solve this problem?
|
|
|
|
```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;
|
|
}
|
|
```
|
|
|
|
___
|
|
|
|
### Breaking cycle - solution
|
|
|
|
* Use `std::weak_ptr<Node>` in one direction
|
|
|
|
```cpp
|
|
#include <memory>
|
|
struct Node {
|
|
std::shared_ptr<Node> child;
|
|
std::weak_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;
|
|
}
|
|
```
|
|
<!-- .element: class="fragment fade-in" -->
|
|
|
|
<div class="box fragment fade-in">
|
|
|
|
==148== All heap blocks were freed -- no leaks are possible
|
|
|
|
</div>
|
|
|
|
___
|
|
|
|
### `std::weak_ptr<>` real life usage (1)
|
|
|
|
```C++
|
|
struct Image {
|
|
/* data */
|
|
};
|
|
|
|
class Screen;
|
|
|
|
class Downloader {
|
|
public:
|
|
void downloadImage(const std::string& url, std::function<void(Image)> callback, std::weak_ptr<Screen> weak_ptr) {
|
|
std::cout << "Start downloading!" << std::endl;
|
|
std::thread(&Downloader::download, this, url, callback, weak_ptr).detach();
|
|
}
|
|
|
|
private:
|
|
void download(const std::string& url, std::function<void(Image)> callback, std::weak_ptr<Screen> weak_ptr) {
|
|
// Simulate download process
|
|
std::cout << "Download in progress!" << std::endl;
|
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
|
std::cout << "Download Finished!" << std::endl;
|
|
if (auto lock = weak_ptr.lock()) {
|
|
callback(Image{});
|
|
} else {
|
|
std::cout << "Screen object expired!\n";
|
|
}
|
|
}
|
|
};
|
|
|
|
class Screen : public std::enable_shared_from_this<Screen> {
|
|
public:
|
|
Screen(Downloader* downloader)
|
|
: downloader_(downloader) {}
|
|
|
|
void printImage(const std::string& url) {
|
|
std::cout << "Waiting for image download!" << std::endl;
|
|
downloader_->downloadImage(
|
|
url, [&](Image image) { onImageDownloaded(std::move(image)); }, weak_from_this());
|
|
}
|
|
|
|
private:
|
|
void onImageDownloaded(Image&& image) {
|
|
// Do some action on image
|
|
std::cout << "Got Image!\n";
|
|
}
|
|
|
|
Downloader* downloader_;
|
|
};
|
|
|
|
int main() {
|
|
auto downloader = std::make_unique<Downloader>();
|
|
auto screen = std::make_shared<Screen>(downloader.get());
|
|
|
|
screen->printImage("www.image.com/sth/special.img");
|
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
|
screen = nullptr;
|
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
|
}
|
|
```
|
|
<!-- .slide: style="font-size: 0.65em" -->
|
|
|
|
___
|
|
|
|
### `std::weak_ptr<>` real life usage (2)
|
|
|
|
Output:
|
|
|
|
```Bash
|
|
Waiting for image download!
|
|
Start downloading!
|
|
Download in progress!
|
|
Download Finished!
|
|
Screen object expired!
|
|
```
|
|
|
|
When we delete line <code>screen = nullptr;</code>
|
|
|
|
```Bash
|
|
Waiting for image download!
|
|
Start downloading!
|
|
Download in progress!
|
|
Download Finished!
|
|
Got Image!
|
|
```
|
|
|
|
___
|
|
|
|
|
|
### `std::weak_ptr<>` real life usage (3)
|
|
|
|
Use std::weak_ptr when:
|
|
|
|
* <!-- .element: class="fragment fade-in" --> Need to observe a lifetime of the object, without having an ownership
|
|
* <!-- .element: class="fragment fade-in" --> Want to break cyclic dependencies
|