[БЕЗ_ЗВУКА] В предыдущем видео мы с вами познакомились с еще одним умным указателем weak_ptr, а в этом видео поговорим о пользовательских делитерах. Пусть у нас есть следующая задача. Есть две функции, которые умеют возвращать Widget, при этом одна из них возвращает Widget по сырому указателю, а вторая возвращает shared_ptr на Widget. И нам нужно написать другую функцию, которая в зависимости от некоторого условия, которое нам будет известно только на этапе выполнения программы, вернет нам результат работы либо одной функции, либо второй функции. Вот так незамысловато мы ее здесь написали. Проверяем условие и возвращаем либо результат первой функции, либо результат второй функции. Казалось бы, а какой тип будет возвращать эта наша новая написанная функция? Давайте подумаем. Допустим, она возвращает сырой указатель. Тогда вызвать функцию, которая возвращает сырой указатель, никаких проблем нет, мы просто вернем тот же самый сырой указатель. Но что делать, если нам нужно вызвать функцию, которая возвращает shared_ptr? Мы можем, конечно, из этого shared_ptr достать сырой указатель с помощью точки Get, мы так уже делали. И это действительно сработает. Но мы вернем сырой указатель, который не будет участвовать во владении этим объектом. А нам бы хотелось, чтобы этот указатель смог поучаствовать во владении. Получается, что сырой указатель нам не подходит. Допустим, будем возвращать тоже shared_ptr. Тогда у нас не будет никаких проблем вызвать функцию, которая сама возвращает shared_ptr, мы просто вернем тот же самый shared_ptr. Но что делать с функцией, которая возвращает сырой указатель? Ведь мы знаем, что нельзя просто так взять и засунуть сырой указатель в shared_ptr. Тем более, если мы вернем shared_ptr, мы знаем, что shared_ptr — это владеющий умный указатель, и когда он будет удален или когда его копии будут удалены, он попытается удалить этот объект. А нам не нужно, чтобы он удалялся, ведь нам возвращают не владеющий указатель. Что же делать? На самом деле мы можем сделать хитрый ход конем и все-таки использовать shared_ptr. Смотрите, за счет чего. Здесь функция возвращает shared_ptr. И если нам нужно вызвать функцию, которая сама возвращает shared_ptr, никаких проблем: просто возвращаем тот же самый shared_ptr. Что делать, если нам нужно вызвать функцию, которая возвращает обычный указатель? Смотрите, мы его вызываем, получаем обычный указатель, и дальше мы этот указатель засунем в новый shared_ptr, который мы создали. Как же так, скажете вы, он же попытается его удалить. И мы знаем, что действительно, умные указатели удаляют объект, на который они ссылаются, когда они заканчивают владение этим объектом. Но это же C++. То есть на самом деле все немного не так, все немного все хитрее. Умные указатели не удаляют объект, когда заканчивают владение объектом, на самом деле они для этого объекта вызывают deleter, который по умолчанию этот объект удаляет. deleter — это просто некоторая функция. И эта функция по умолчанию реализована таким образом, что она просто вызывает delete для переданного указателя. А здесь мы с вами заведем свой deleter. Смотрите, мы пишем auto dummyDeleter = и некоторая пустая функция, которая принимает указатель и не делает ничего. То есть если бы мы писали deleter по умолчанию, мы бы написали внутри delete этот указатель. А здесь мы не делаем ничего. То есть получается, мы создаем такой shared_ptr, который указывает на некоторый объект и который, когда он заканчивает владение этим объектом, вызывает дня него deleter, который не делает ничего. То есть по сути мы создали shared_ptr, который этим объектом не владеет. Получается, что в первой ветке мы возвращаем владеющий shared_ptr, который нам вернула функция, а во второй ветке мы возвращаем не владеющий shared_ptr, то есть мы возвращаем некоторый shared_ptr, который условно владеет объектом. Обратите внимание, что в данном случае мы нарушили соглашение по владению динамическими объектами. Ну потому что как бы предполагается, что shared_ptr безусловно владеет объектами, на которые он ссылается. Однако здесь у нас была определенная причина. Мы решили, что для реализации такого поведения программы мы нарушим эти соглашения. И это может быть оправдано в зависимости от задачи, которую мы решаем. Итак, мы узнали, что существует пользовательский deleter, который позволяет выполнить некоторое произвольное действие вместо удаления объекта. Использование deleter на самом деле позволяет нам создать условно владеющий shared_ptr, хотя это приведет к нарушению соглашения по владению. И на самом деле deleter доступен не только для shared_ptr, но для unique_ptr тоже можно указать свой deleter. Правда, работать это будет немножко по-другому, и сейчас мы на этом подробно останавливаться не будем, это выходит за рамки курса. Ну и поскольку это последнее видео в данном блоке про умные указатели, я хочу закончить его небольшим напутствием. Всегда старайтесь следовать соглашению по владению динамическими объектами. На практике их нужно будет нарушать разве что в тех случаях, когда вам придется взаимодействовать с компонентами, которые уже по тем или иным причинам нарушают эти соглашения. Например, это может быть компонента, написанная с использованием старого стандарта C++, еще до C++ 11, когда не было умных указателей. Или это может быть компонента, написанная на чистом C, где, очевидно, нет никаких умных указателей. В этом случае рекомендуется эти компоненты в явном виде изолировать от всего остального кода и во всем остальном коде придерживаться этих соглашений. На этом все, я с вами прощаюсь, успехов и хорошего кода.