From f6f62e78e9dcb1df6552eedaa6b551f608a91d82 Mon Sep 17 00:00:00 2001 From: Nekrolm Date: Fri, 11 Oct 2024 01:25:06 +0100 Subject: [PATCH] Add shared_from_this --- README.md | 23 +- standard_lib/shared_from_this.md | 357 +++++++++++++++++++++++++ standard_lib/shared_ptr_constructor.md | 10 +- 3 files changed, 373 insertions(+), 17 deletions(-) create mode 100644 standard_lib/shared_from_this.md diff --git a/README.md b/README.md index f26c4f0..63dd7dd 100644 --- a/README.md +++ b/README.md @@ -70,17 +70,18 @@ 6. Стандартная библиотека 1. [NULL-терминированные строки](standard_lib/null_terminated_string.md) 2. [Конструирование std::shared_ptr](standard_lib/shared_ptr_constructor.md) - 3. [потоки ввода/вывода](standard_lib/iostreams.md) - 4. [std::aligned_storage](standard_lib/aligned_storage.md) - 5. [функции стантарной библиотеки как параметры](standard_lib/function_pass_and_address_restriction.md) - 6. [std::ranges::views](standard_lib/ranges_views_lazy.md) - 7. [`operator[] ` ассоциативных контейнеров](standard_lib/map_subscript.md) - 8. [std::enable_if/std::void_t](standard_lib/enable_if_void_t.md) - 9. [Конструкторы контейнеров](standard_lib/stl_constructors.md) - 10. [std::uniform_int_distribution](standard_lib/uniform_int_distribution.md) - 11. [std::ranges::transform | filter](standard_lib/transform_filter_ranges.md) - 12. [vector::reserve и vector::resize](standard_lib/vector_resize_reserve.md) - 13. [std::function](standard_lib/std_function_const.md) + 3. [shared_from_this](standard_lib/shared_from_this.md) + 4. [потоки ввода/вывода](standard_lib/iostreams.md) + 5. [std::aligned_storage](standard_lib/aligned_storage.md) + 6. [функции стантарной библиотеки как параметры](standard_lib/function_pass_and_address_restriction.md) + 7. [std::ranges::views](standard_lib/ranges_views_lazy.md) + 8. [`operator[] ` ассоциативных контейнеров](standard_lib/map_subscript.md) + 9. [std::enable_if/std::void_t](standard_lib/enable_if_void_t.md) + 10. [Конструкторы контейнеров](standard_lib/stl_constructors.md) + 11. [std::uniform_int_distribution](standard_lib/uniform_int_distribution.md) + 12. [std::ranges::transform | filter](standard_lib/transform_filter_ranges.md) + 13. [vector::reserve и vector::resize](standard_lib/vector_resize_reserve.md) + 14. [std::function](standard_lib/std_function_const.md) 7. Исполнение программы 1. [Бесконечные циклы](runtime/endless_loop.md) 2. [Рекурсия](runtime/recursion.md) diff --git a/standard_lib/shared_from_this.md b/standard_lib/shared_from_this.md new file mode 100644 index 0000000..fa8ec81 --- /dev/null +++ b/standard_lib/shared_from_this.md @@ -0,0 +1,357 @@ +# std::shared_from_this + +Обсуждая особенности [`std::make_shared`](shared_ptr_constructor.md), я упоминул, что иногда крайне необходимо убедиться, что объекты вашего класса всегда создаются только в куче и управляются с помощью умного указателя. Вот сейчас будет еще один такой случай. + +Вы разрабатываете графический интерфейс и, как это было принято лет 20 назад, решили что все доллжно быть объектно-ориентированно и красиво. Компонентики. Виджеты. Всех мы будем по требованию создавать, управлять умными shared и weak указателями, чтоб не очень сильно задумываться о владении — очень стандартный подход, между прочим. Rust библиотеки для GUI, например, часто [критикуют](https://www.warp.dev/blog/why-is-building-a-ui-in-rust-so-hard) за чудовищную сложность именно из-за владений. + +И так у вас появились некоторые базовые типы: + +```C++ +enum class EventType { + Clicked, + Created, + // и другие +}; + +class Widget { +public: + virtual ~Widget() = default; +}; + +class EventListener { +public: + // Уведомить Listener, что такой-то Widget породил некоторое событие + void notify(EventType, std::weak_ptr event_source); +}; +``` + +Хорошо. Дальше давайте заведем кнопку! Куда же без кнопки в хорошем UI?! + +```C++ +class Button: public Widget { +public: + Button(std::shared_ptr listener): listener_ {listener} + { + // (1) хотелось бы уведомить listener, что кнопка создана! + listener_->notify(EventType::Created, this); // Это неправильно... + } + + void click() { + // (2) хотелось бы уведомить listerner, что на кнопочку нажали! + listener_->notify(EventType::Clicked, this); // И это разумеется неправильно + } + +private: + std::shared_ptr listener_; +}; +``` + +К нашему большому счастью, этот [код не компилируется](https://godbolt.org/z/v77aG9rhz) + +``` +:26:47: error: cannot convert 'Button*' to 'std::weak_ptr' + 26 | listener_->notify(EventType::Created, this); // Это неправильно... + | ^~~~ + | | + | Button* +``` + +Ах, ну да, типы разные... Опытного разработчика на C++ это, конечно, заставило бы задуматься. А вот неопытного... Он может просто выполнить «преобразование» типов! + +```C++ + Button(std::shared_ptr listener): listener_ {listener} + { + // (1) хотелось бы уведомить listener, что кнопка создана! + listener_->notify(EventType::Created, std::shared_ptr