Слабая ссылка на scoped_ptr?

Обычно я следую руководству по стилю Google, которое, как мне кажется, хорошо согласуется с тем, как я вижу вещи. Я также почти всегда использую boost::scoped_ptr, так что только один менеджер имеет право собственности на конкретный объект. Затем я передаю голые указатели, идея заключается в том, что мои проекты структурированы таким образом, что менеджеры указанных объектов всегда уничтожаются после уничтожения объектов, которые их используют.

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Smart_Pointers

Это все здорово, однако меня только что укусила неприятная маленькая ошибка топания памяти, когда владелец просто был удален до того, как объекты, которые его использовали, были удалены.

Теперь, прежде чем все будут прыгать вверх и вниз, что я дурак для этого шаблона, почему бы мне просто не использовать shared_ptr? и т. д., учтите, что я не хочу иметь неопределенную семантику владельца. Несмотря на то, что shared_ptr уловил бы этот конкретный случай, он отправляет неправильное сообщение пользователям системы. Он говорит: «Я не знаю, кому это принадлежит, это может быть ты!»

То, что помогло бы мне, было бы слабым указателем на указатель области. По сути, указатель с областью действия, который имеет список слабых ссылок, которые обнуляются при разрушении указателя с областью действия. Это позволило бы семантику единого владения, но дало бы использующим объектам возможность уловить проблему, с которой я столкнулся.

Таким образом, за счет дополнительного указателя «weak_refs» для scoped_ptr и дополнительного указателя «next_weak_ptr» в weak_ptr получится изящная маленькая многопользовательская структура с одним владельцем.

Это может быть даже просто функция отладки, поэтому в «выпуске» вся система просто превращается обратно в scoped_ptr нормального размера и стандартный одиночный указатель для слабой ссылки.

Итак..... мои ВОПРОСЫ после всего этого:

  1. Есть ли такой указатель/шаблон уже в stl/boost, который мне не хватает, или я должен просто свернуть свой собственный?
  2. Есть ли лучший способ, который по-прежнему соответствует моей цели единоличного владения?

Привет, Шейн


person Shane    schedule 28.07.2011    source источник
comment
Я не знаю стандартной или библиотечной реализации. Если вы реализуете то, что вы описали, вы получите реализацию, очень похожую на boost shared_ptr/weak_ptr, поскольку вы, вероятно, захотите обнаружить случай, когда кто-то получил необработанный указатель из weak_ptr и кэшировал его. Сказав, что вы можете просто захотеть использовать shared_ptr/weak_ptr и утверждать в своем менеджере, что shared_ptr, который он сохраняет, уникален, когда вы хотите уничтожить объект.   -  person Dan O    schedule 28.07.2011


Ответы (4)


2. Есть ли лучший способ, который по-прежнему соответствует моей цели единоличного владения?

Используйте shared_ptr, но как член класса, чтобы он был частью инварианта этого класса, а общедоступный интерфейс предоставлял только способ получения weak_ptr.

Конечно, патологический код может сохранять свои собственные shared_ptr из этого weak_ptr столько, сколько захочет. Я не рекомендую здесь защищаться от Макиавелли, только от Мерфи (по словам Саттера). С другой стороны, если ваш вариант использования асинхронный, то тот факт, что блокировка weak_ptr возвращает shared_ptr, может быть особенностью!

person Luc Danton    schedule 28.07.2011
comment
На самом деле это хороший способ скрыть проблему владения. Мне это нравится :) - person Shane; 31.07.2011

Несмотря на то, что shared_ptr уловил бы этот конкретный случай, он отправляет неправильное сообщение пользователям системы. Он говорит: «Я не знаю, кому это принадлежит, это может быть ты!»

Shared_ptr не означает «я не знаю, кому это принадлежит». Это означает "Нам это принадлежит". Тот факт, что один объект не имеет исключительного права собственности, не означает, что им может владеть каждый.

Цель shared_ptr — гарантировать, что указатель не может быть уничтожен до тех пор, пока все, кто его разделяет, не согласятся с тем, что его следует уничтожить.

Есть ли такой указатель/шаблон уже в stl/boost, который мне не хватает, или я должен просто свернуть свой собственный?

Вы можете использовать shared_ptr точно так же, как вы используете scoped_ptr. Тот факт, что им можно поделиться, не означает, что вы должны им поделиться. Это был бы самый простой способ работать; просто сделайте единоличное владение соглашением, а не правилом, установленным API.

Однако, если вам нужен указатель с одним владельцем, но со слабыми указателями, в Boost его нет.

person Nicol Bolas    schedule 28.07.2011
comment
Привет, Николь, да, я был несколько мелодраматичен в своей фразе «Я не знаю, кому принадлежит эта... линия», но да, ты прав ;) Что касается твоего мнения, я начинаю переосмысливать свою практику обеспечения единоличного владения и просто катаемся с подходом shared_ptr. - person Shane; 31.07.2011

Я не уверен, что слабые указатели так сильно помогут. Как правило, если компонент X использует другой компонент Y, X должен быть проинформирован о кончине Y не только для того, чтобы аннулировать указатель, но, возможно, чтобы удалить его из списка или изменить его режим работы, чтобы он больше не нуждался в объекте. . Много лет назад, когда я впервые начал работать с C++, я пытался найти хорошее универсальное решение. (Тогда эта проблема называлась управлением отношениями.) Насколько я знаю, не всегда было найдено хорошее универсальное решение; по крайней мере, в каждом проекте, над которым я работал, использовалось решение, созданное вручную на основе шаблона Observer.

У меня действительно был ManagedPtr на моем сайте, когда он еще работал, который вел себя примерно так, как вы описываете. На практике, за исключением частного случая, который привел к этому, я так и не нашел ему реального применения, потому что уведомление всегда было нужно. Однако это не сложно реализовать; управляемый объект является производным от класса ManagedObject и получает все указатели (ManagedPtr, а не необработанные указатели), которые он передает от него. Сам указатель зарегистрирован в классе ManagedObject, а деструктор класса ManagedObject посещает их все и «отключает» их, устанавливая фактический указатель в нуль. И, конечно же, ManagedPtr имеет функцию isValid, так что клиентский код может протестировать перед разыменованием. Это работает хорошо (независимо от того, как объект управляется, большинство моих сущностных объектов «владеют» сами собой, и делают delete this ответ на какой-то конкретный ввод), за исключением того, что вы склонны к утечке недопустимых ManagedPtr (например, всякий раз, когда клиент сохраняет указатель в какой-либо контейнер, потому что он может иметь более одного), и клиенты по-прежнему не уведомляются, если им нужно предпринять какие-либо действия, когда ваш объект умирает.

person James Kanze    schedule 28.07.2011
comment
Привет, Джеймс, я реализовал аналогичный контейнер, который эффективно обнуляет все ссылки/указатели после вызова такой функции, как функция objectDeleting. На самом деле это просто шаблон наблюдателя. Я начинаю думать, что это не обязательно и для моего менеджера. - person Shane; 31.07.2011

Если вы используете QT, QPointer в основном является слабым указателем на QObject. Он подключается к событию «Меня только что уничтожили» в указанном значении и автоматически аннулирует себя по мере необходимости. Это действительно здоровенная библиотека, которую можно использовать для отслеживания ошибок, если вы еще не прыгаете через обручи QT.

person Dennis Zickefoose    schedule 28.07.2011