@pixik

Какими приёмами вы пользуетесь чтобы различать указатели, которые нужно освобождать, от указателей, которыми нужно только пользоваться?

Доброго времени!
Часто бывает такое, что один объект порождает другой, а третий использует этот объект.
К примеру, объект a создал объект b. А некий третий объект с использует объект b в своих нуждах. Но освобождает память объекта b только a и никто больше.
Возможно есть какие то очень подходящие для меня механизмы из stl или boost, которых я не знаю.
Простой std::shared_ptr не подходит, т.к. я хочу, чтобы ответственность за хранение объектов была чёткой, а не объект следил за собой (в конкретно этом случае).
Возможно я ошибаюсь в своих хотелках. Хочется разобраться как быть лучше в описанной выше ситуации.
  • Вопрос задан
  • 264 просмотра
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
pixik , витиеватый, очень размытый вопрос. Что же тебя все-таки интересует? Как другие люди организуют схемы владения? Или каким образом лучше организовать удаление объекта при разделенном владении.
Тебе суть вопроса, скорее всего, понятна. Но это лишь потому что у тебя есть контекст этого вопроса, а у всех остальных его нет и он явно не создается.

Так вот.
Если один объект (A) безраздельно владеет другим (B), то второму положено быть частью первого. Так просто легче жить.
Если вторым (B) является указатель, то в первом (A) используется std::unique_ptr опять-же для упрощения себе жизни.
При этом, вложенный объект (B) из родительского отдается по голому указателю. Ну да, это норма, хоть мои слова и встретят бурю негодования нетерпеливых читателей.
В это же самое время появляется риск схлопотать AV/SEGV когда для голого указателя (B) появляются клиенты (C), время жизни которых значительно больше времени жизни (A). Все просто, (A) живет меньше семейства (C), следовательно сохраненный в (C) голый указатель на (B) обязательно приведет к AV/SEGV после окончания времени жизни (A).

Вот, проблема оглашена. Решаем.

Решение первое: std::shared_ptr и std::weak_ptr.
std::shared_ptr является примитивом разделения владения. Будем хранить (B) внутри него. А наружу (A) будет отдавать st::weak_ptr с указателем на (B). Клиенты семейства (C) будут держать (B) внутри сохраненных std::weak_ptr. Таким образом все семейство (С) всегда будет защищено от обращения к удаленному (B).

Решение второе: два std::sared_ptr.
Ну мало ли... Я знаю несколько случаев когда именно такая схема является максимально эффективной. В частности, бывает необходимо сделать именно разделенное владение внутри всего семейства (C), давая информацию для (A) о нужности оставлять (B) в живых. Пулы, кеши по владению, мало ли таких задач...
Эта схема достаточно сложна и требует содержать std::weak_ptr на каждый внешний std::shared_ptr для обеспечения правильной работы схемы.
Решение сводится к тому, чтобы уже имеющийся std::shared_ptr обернуть в другой с заменой деконструктора.
(A) отдает (B) через std::shared_ptr, но в момент запроса (A) оборачивает реальный std::shared_ptr (B) в новый, используя голый указатель на (B) и свой сторонний деконструктор. Внутри этого стороннего деконструктора делаются все нужные манипуляции над (B). Этот сторонний деконструктор вызовется тогда, когда все семейство (C) избавится от ссылки на (B).

Решение третье: когда (A) сам лежит внутри std::shared_ptr и унаследован от std::enable_shared_from_this.
Тут все просто, для выдачи (B) из (A) можно использовать делегируемый std::shared_ptr, который сконструирован из указателя на (B) и std::shared_ptr (A) полученного из функции shared_from_this().
В этом случае (A) не помрет до тех пор, пока семейство (C) не отпустит (B).

Как видишь, корень твоего вопроса решается всеми тремя способами. На самом деле способов еще больше, но самое важное что все способы дают стандартное решение. Велосипеды в этом вопросе - это от неграмотности.

Советую максимально подробно изучить раздел управления памятью в STL: en.cppreference.com/w/cpp/memory

Принимаю вопросы и уточнения. :)
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
@Mercury13
Программист на «си с крестами» и не только
Но есть ещё один указатель, который реализует именно что единую ответственность — std::unique_ptr. Также я использую одну самоделку — но, возможно, «не умею готовить» unique_ptr.
Ответ написан
xmoonlight
@xmoonlight
https://sitecoder.blogspot.com
Дайте "рычаги" порождающему: чтобы "с" попросил "a" что-то сделать над "b".
Ответ написан
Комментировать
vt4a2h
@vt4a2h Куратор тега C++
Senior software engineer (C++/Qt/boost)
Все виды умных указателей (да и вообще все что использует RAII) -- это всего лишь еще одно удобство чтобы писать мень кода и чтобы он работал надёжнее. Управление временем жизни объектов всё еще на плечах программиста. Ведь даже используя умные укзатели вы можете получить утечку ресурсов, например за счёт циклических ссылок (и очень хитрых циклических ссылок! :)) и т.п. + многие забывают про weak_ptr...
В общем и целом, проблема, которую вы описали -- это вопрос архитектуры. Т.е. знание кто и за что отвечает заложено в вашей системе и она работает по контрактам, которые описаны внутри.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы