На текущий момент мы с вами познакомились с основными и наиболее полезными возможностями умных указателей, которые приходится часто использовать на практике. В этом видео мы поговорим о некоторых дополнительных возможностях, которые на практике используются не так часто, но если они нужны, то лучше знать о том, как их делать, иначе без них будет достаточно тяжело. И цель этого видео не в том чтобы о них рассказывать подробно, а кратко показать, что можно с их помощью делать для того чтобы когда у вас на практике возникнет в них необходимость, вы вспомнили, ага, что-то такое было, что-то такое знаю, ничего не помню, но сейчас почитаю и станет понятно. Конкретно мы поговорим о двух вещах, это о еще одном умном указатель weak_ptr и о пользовательском deleter, который можно использовать shared_ptr. Так, начнем с weak_ptr. Пусть у нас есть следующий пример, некоторый класс, который кеширует widget-ы, у него есть функция GetWidget и передается имя и она возвращает widget с данным именем, если у нее этот widget уже есть, она возвращает готовый widget shared_ptr на него, если же этого widget-а нет - она его создает. И внутри себя она хранит map-ку, строке соответствует shared_ptr на widget. Это отлично работает, но есть одна небольшая проблема. Даже если мы создали какой-то widget и в программе его уже больше не используем, этот widget все равно будет существовать, потому что на него сохраняется shared_ptr внутри этого объекта кеша, то есть widget мы не используем, но он все равно у нас висит, как бы, и зачем возможно он отжирает некоторые ресурсы. Нам бы хотелось сделать так, что если в остальной программе мы перестали пользоваться этим widget-ом, чтобы этот widget удалялся. Как это можно сделать? Первая мысль, которая приходит в голову, почему бы нам не использовать shared_ptr::use_count(). Мы уже видели видео про многопоточность, что существует use_count() и он вернет нам текущее количество ссылок из shared_ptr на данный объект. Если мы видим, что этот use_count() == 1 - это значит, что только внутри нашего кеша хранится shared_ptr на этот объект, а больше в программе нигде не используется, и соответственно, мы можем его удалить. Ну казалось бы да, но есть некоторая проблема. Мы же не знаем точный момент, когда в остальной программе у нас уничтожится последний shared_ptr, который указывает на этот widget, то есть непонятно когда этот use_count() вызывать. Мы можем подумать, ну ладно, давайте заведем какой-нибудь фоновый поток в котором будем по таймеру смотреть на use_count(), хорошо, так в принципе можно сделать, но тут возникает другая проблема, как мы уже знаем use_count() в многопоточной среде использовать очень не надёжно, потому что значение, которое возвращает use_count() оно устаревает ровно в тот момент, когда мы его получили, ибо ровно в этот же самый момент из другого потока кто-то может у нас запросить этот же самый widget, и соответственно, счетчик ссылок у нас увеличится на одиночку. Получается что, если нам нужен use_count(), нам нужен фоновый поток, но если фоновый поток - мы не можем использовать use_count(). Есть другое решение, более подходящее в данном случае, это как раз с использованием weak_ptr. weak_ptr - это не владеющий умный указатель. Казалось бы, он вроде бы умный, но почему он тогда не владеющий? Чем он лучше обычного сырого указателя? Дело в том, что из weak_ptr можно корректно создать shared_ptr, который уже будет владеющим. Как мы знаем, из сырого указателя создавать shared_ptr - это гиблое дело. Это вообще считается низкоуровневым управлением динамической памятью, потому что каждый раз, когда мы создаем shared_ptr из сырого указателя у нас для этого объекта заводится свой контрольный блок, и созданный таким образом shared_ptr оказываются не связанными друг с другом, и они скорее всего приведут к двойному удалению объекта. А от из weak_ptr можно абсолютно корректно создавать shared_ptr и все эти shared_ptr будут иметь один и тот же контрольный блок. Давайте посмотрим, как у нас изменится код с использованием weak_ptr. Смотрите, теперь у нас в map-ке мы храним уже не shared_ptr, а weak_ptr и посмотрим на реализацию функцию GetWidget. Первым делом мы обращаемся к элементу map-ки с данным name, это будет weak_ptr и мы вызываем у него метод lock. Метод lock у weak_ptr как раз создает shared_ptr, для того объекта, на который указывает этот weak_ptr, если такой объект есть, соответственно, если такой объект есть, то мы получаем у него него shared_ptr и возвращаем и все хорошо. А вот если его, если этого объекта нет, что происходит? Объекта может не быть по двум причинам: либо этот объект еще не был создан вообще, в принципе мы только создали кэш и в кэше widget-а с этим именем нет, либо и это самое главное, этот объект когда-то был создан, но с тех пор им перестали пользоваться, то есть мы когда-то отдали на него shared_ptr, но потом этот shared_ptr и все его копии были уничтожены и объектом больше никто не пользуется, счетчик ссылок упал до нуля, тогда этот объект будет удален и weak_ptr сможет понять, что объект был удален. Соответственно, в обоих этих случаях метод lock вернет нам пустой shared_ptr, это будет обозначать, что объекта у нас нет, по той или иной причине. В этом случае мы зайдем в условия в if, и поскольку у нас объекта нет, но соответственно, его нужно создать, поэтому мы точно таким же образом вызываем функцию make_shared для widget с данным именем, она возвращает shared_ptr, этот shared_ptr мы неявно конвертируем в weak_ptr и складываем его в map-ку. После этого мы этот shared_ptr возвращаем. Вот таким не сложным исправление мы смогли добиться ровно того поведения этой программы, которая нам нужна, за счет использования еще одного специфического умного показателя weak_ptr. А в следующем видео мы с вами поговорим о пользовательских deleter-ах.