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
Принимаю вопросы и уточнения. :)