## Cyclic dependencies cyclic dependencies * Cyclic dependency is where you have class A with self-referencing member. * Cyclic dependency is where you have two classes A and B where A has a reference to B which has a reference to A. * How to fix it? ___ ### `std::weak_ptr<>` to the rescue * does not own an object * observes only * must be converted to std::shared_ptr<> to access the object * can be created only from a std::shared_ptr<>
weak pointers
___ ### `std::weak_ptr<>` usage
```cpp #include #include struct Msg { int value; }; void checkMe(const std::weak_ptr & wp) { std::shared_ptr p = wp.lock(); if (p) std::cout << p->value << '\n'; else std::cout << "Expired\n"; } int main() { auto sp = std::shared_ptr{new Msg{10}}; auto wp = std::weak_ptr{sp}; checkMe(wp); sp.reset(); checkMe(wp); } ```
```bash > ./a.out 10 Expired ```
___ ### `std::shared_ptr<>` cyclic dependencies * How to solve this problem? ```cpp #include struct Node { std::shared_ptr child; std::shared_ptr parent; }; int main () { auto root = std::shared_ptr(new Node); auto child = std::shared_ptr(new Node); root->child = child; child->parent = root; } ``` ___ ### Breaking cycle - solution * Use `std::weak_ptr` in one direction ```cpp #include struct Node { std::shared_ptr child; std::weak_ptr parent; }; int main () { auto root = std::shared_ptr(new Node); auto child = std::shared_ptr(new Node); root->child = child; child->parent = root; } ```
==148== All heap blocks were freed -- no leaks are possible
___ ### `std::weak_ptr<>` real life usage (1) ```C++ struct Image { /* data */ }; class Screen; class Downloader { public: void downloadImage(const std::string& url, std::function callback, std::weak_ptr 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 callback, std::weak_ptr 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 { 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(); auto screen = std::make_shared(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)); } ``` ___ ### `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 screen = nullptr; ```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: * Need to observe a lifetime of the object, without having an ownership * Want to break cyclic dependencies