Подтвердить что ты не робот

Что произойдет, если объект, удерживаемый интеллектуальным указателем, будет удален в другом месте?

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

4b9b3361

Ответ 1

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

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

Что я обычно делал, чтобы попытаться сделать код безопасным для использования при использовании чего-то вроде Qt, чтобы использовать теперь устаревший auto_ptr (unique_ptr заменяет его и гораздо безопаснее, если у вас есть С++ 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 уничтожается через сигнал уничтожения и не пытается удалить объект во второй раз. Тем не менее, он может получить волосатый, полагающийся на этот сигнал в многопоточном коде, и если вы реализуете общий указатель, это может стать довольно сложной задачей, связанной с подсчетом атомных ссылок, захватом функции удаления на сайте, который создается указатель (чтобы избежать модуля граничные новые/исключающие несоответствия) и т.д.

Ответ 2

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

Ответ 3

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

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

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