2012-03-02 3 views
12

Этот вопрос всегда беспокоит меня особенно, когда я программирую Qt. Поскольку Qt использует деревья собственности объекта, передавая указатель, например. через myBoostSharedPtr.get() может косвенно передавать право собственности. Теперь рассмотрим случай, когда какой-то объект Qt уничтожается, а все дерево объектов уничтожено, но интеллектуальный указатель все еще жив, например. как член другого класса. Что произойдет, если умный указатель будет удален после этого? Двойное удаление со всеми неприятными последствиями? Предотвращают ли некоторые реализации интеллектуальных указателей это?Что происходит, если объект, удерживаемый интеллектуальным указателем, удаляется в другом месте?

+1

Я действительно столкнулся с этим типом проблемы с 'QSharedPointer', который концептуально очень похож на' shared_ptr' boost. Я написал запись в блоге о моих выводах: http://blog.codef00.com/2011/12/15/not-so-much-fun-with-qsharedpointer/ –

ответ

18

Я настолько соблазнился, чтобы рассказать о слабых сторонах модели памяти Qt, где многие API по-прежнему принимают необработанные указатели с ожиданием того, что клиент выделяет его, а QObject, который принял указатель, удаляет его.

Ответ на ваш вопрос: undefined поведение. shared_ptr не имеет механизма для обнаружения, если указатель удаляется чем-то другим, кроме самого shared_ptr, поэтому он обычно пытается освободить указатель во второй раз (вызов delete на висящем указателе). Если вы хотите использовать shared_ptr, вы должны придерживаться shared_ptr как единственного менеджера памяти. Это справедливо даже для собственного Qt QSharedPointer.

То, что я обычно делал, чтобы попытаться получить мой код достаточно исключение безопасным при использовании что-то вроде Qt, чтобы использовать теперь осуждается auto_ptr (unique_ptr заменяет его и намного безопаснее, если у вас есть C++ 11 доступны). Это единственное место, где я когда-либо испытывал желание использовать auto_ptr раньше, так как он предоставляет метод release.

unique_ptr<QListWidget> widget(new QListWidget(...)); 
// do stuff with the widget to set it up for your GUI 
some_layout.addWidget(widget.release()); // <-- release ownership so that 
             // the layout now becomes responsible 
             // for memory management 
// ^^ auto_ptr works above if we don't have C++11 

Если вам нужно держать постоянный указатель на объект после того, как она уже память управляется Qt (например: указатель на ваш виджет, после того, как вы вставили его в макете), просто использовать обычный указатель , Там не намного лучше вы можете сделать, так как Qt теперь является менеджером памяти для этого объекта.

Однако вы можете обнаружить, когда объект уничтожен (и, следовательно, когда указатель недействителен) через сигнал QObject::destroyed.

Если вы хотите получить очень сложный, вы можете создать общий тип указателя, который хранит только подклассы QObject. Поскольку QObject предоставляет разрушенный сигнал, этот вид пользовательского интеллектуального указателя может обнаруживать, когда QObject уничтожается через сигнал уничтожения и не пытается удалить объект во второй раз. Тем не менее, он может получить волосатый, полагающийся на этот сигнал в многопоточном коде, и если вы реализуете общий указатель, это может стать довольно сложной задачей, связанной с подсчетом атомных ссылок, захватом функции удаления на сайте, который создается указатель (чтобы избежать модуля граница new/delete mismatches) и т. д.

+0

Я не думаю, что вы должны опубликовать в списке аргументов , Если 'addWidget' бросает, прежде чем он перехватит указатель, вы просочитесь. – GManNickG

+1

@GmanNickG это хороший момент. Как вы думаете, мы должны освободиться только после того, как звонок будет успешным? Как я смотрю на это, API принимает исходные указатели на память, выделенные клиентом. Я полагаю, что, пройдя его, ответственность QT заключается в вызове delete на указателе, если в * addWidget * возникает исключение. В противном случае это QT утечка, а не я, насколько я вижу, и в документации не указано, пытается ли QT обработать это условие, удалив указатель с локальным блоком try/catch в addWidget. Если он делает и удаляет указатель в addWidget и rethrows, мой unique_ptr может вызвать delete .. – stinky472

+0

@GManNickG ... на свисающем указателе. Это будет сильно зависеть от того, что я знаю о том, как реализуется addWidget. – stinky472

1

Да, скорее всего, и нет, они этого не делают, и я не вижу, как они могут. Вы должны полагаться на контракт, что то, что вы передаете указателю, не будет принадлежать (если в документации не указано), и если последнее, не оберните его интеллектуальным указателем на вашей стороне.

1

Вы не можете иметь совместное владение между родительским QObject и интеллектуальным указателем и не мешать удалению другого, но вы можете отслеживать удаление любого QObject, используя только QWeakPointer (или QPointer).

См http://qt-project.org/doc/qt-4.8/qweakpointer.html#tracking-qobject

Update: С Qt 5, отслеживая QObject ы не управляется QSharedPointer с QWeakPointer осуждается в пользу QPointer (который сам по себе undeprecated).

+0

Вы не должны. Использование 'QWeakPointer' без' QSharedPointer' было устарело по очень веским причинам, по той же причине это не в _C++ 11_. – abergmeier

+0

@abergmeier Я не уверен, почему вы проголосовали вместо того, чтобы просто оставить комментарий или даже обновить ответ самостоятельно. Использование 'QWeakPointer' для отслеживания' QObject' было рекомендуемым способом на момент написания. – alexisdm

Смежные вопросы