Как delete и deleteLater работает с сигналами и слотами в Qt?

Существует объект classа QNetworkReply. Существует слот (в каком-то другом объекте), подключенный к его завершенному () сигналу. Сигналы синхронны (по умолчанию). Существует только один stream.

В какой-то момент я хочу избавиться от обоих объектов. Больше никаких сигналов или чего-либо от них. Я хочу, чтобы они ушли. Ну, я думал, я буду использовать

delete obj1; delete obj2; 

Но могу ли я действительно? Спецификации для ~ QObject говорят:

Удаление QObject во время ожидания ожидающих событий может привести к сбою.

Что такое «ожидающие события»? Может ли это означать, что, пока я вызываю свое delete , уже есть некоторые «ожидающие события», которые могут быть доставлены, и что они могут вызвать сбой, и я не могу проверить, есть ли они?

Итак, скажем, я звоню:

 obj1->deleteLater(); obj2->deleteLater(); 

Чтобы быть в безопасности.

Но я действительно в безопасности? Элемент deleteLater добавляет событие, которое будет обрабатываться в основном цикле, когда элемент управления попадает туда. Могут ли быть какие-то ожидающие события (сигналы) для obj1 или obj2 уже там, ожидающие обработки в основном цикле до того, как будет обработано deleteLater? Это было бы очень неудачно. Я не хочу писать проверку кода для статуса «несколько удаленных» и игнорирования входящего сигнала во всех моих слотах.

Удаление QObjects обычно безопасно (т. Е. В обычной практике, могут быть патологические случаи, о которых я не знаю), если вы следуете двум основным правилам:

  • Никогда не удаляйте объект в слоте или методе, вызываемом прямо или косвенно сигналом (синхронным, тип подключения «прямой») от объекта, который нужно удалить. Например, если у вас есть class Operation с сигналом Operation :: finished () и slot Manager :: operationFinished (), вы не хотите удалять объект операции, который излучал сигнал в этом слоте. Метод, испускающий законченный () сигнал, может продолжать доступ к «этому» после испускания (например, доступ к элементу), а затем работать с недопустимым «этим» указателем.

  • Аналогично, никогда не удаляйте объект в коде, который синхронно вызывается из обработчика события объекта. Например, не удаляйте SomeWidget в своем SomeWidget :: fooEvent () или в методах / слотах, которые вы вызываете оттуда. Система событий продолжит работу с уже удаленным объектом -> Crash.

Оба могут быть сложными для отслеживания, так как обратные трассы обычно выглядят странно (например, сбой при доступе к переменной-члену POD), особенно если у вас сложная цепочка сигналов / слотов, где может произойти удаление нескольких шагов, первоначально инициированных сигналом или событием из объект, который удален.

Такие случаи являются наиболее распространенным вариантом использования deleteLater (). Он гарантирует, что текущее событие может быть выполнено до того, как элемент управления вернется в цикл событий, который затем удалит объект. Другой, я считаю, что лучший способ – отложить все действие с помощью очередного соединения / QMetaObject :: invokeMethod (…, Qt :: QueuedConnection).

Следующие две строки ваших упомянутых документов говорят ответ.

Из ~ QObject ,

Удаление QObject во время ожидания ожидающих событий может привести к сбою. Вы не должны удалять QObject напрямую, если он существует в другом streamе, чем тот, который выполняется в настоящий момент. Вместо этого используйте deleteLater (), из-за чего цикл цикла будет удалять объект после отправки всех ожидающих событий.

В нем конкретно говорится, что мы не должны удалять из других streamов. Поскольку у вас есть однопоточное приложение, безопасно удалять QObject .

Кроме того, если вам нужно удалить его в многопоточной среде, используйте deleteLater() который удалит ваш QObject после обработки всех событий.

Вы можете найти ответ на свой вопрос, прочитав об одном из правил объекта Delta, в котором говорится следующее:

Сигнальный сейф (SS).
Должно быть безопасно вызывать методы на объекте, включая деструктор, из слота, вызываемого одним из его сигналов.

Фрагмент:

По сути, QObject поддерживает удаление при передаче сигналов. Чтобы воспользоваться этим, вы должны быть уверены, что ваш объект не попытается получить доступ к каким-либо из его собственных членов после его удаления. Однако большинство объектов Qt не написаны таким образом, и для них нет необходимости. По этой причине рекомендуется всегда вызывать deleteLater (), если вам нужно удалить объект во время одного из своих сигналов, потому что вероятность того, что «удалить» просто приведет к краху приложения.

К сожалению, не всегда ясно, когда вы должны использовать ‘delete’ vs deleteLater (). То есть, не всегда очевидно, что путь кода имеет источник сигнала. Часто у вас может быть блок кода, который использует «удаление» на некоторых объектах, которые сегодня безопасны, но в какой-то момент в будущем этот же блок кода заканчивается тем, что вызывается из источника сигнала, и теперь внезапно ваше приложение рушится. Единственное общее решение этой проблемы – использовать deleteLater () все время, даже если с первого взгляда это кажется ненужным.

Обычно я рассматриваю правила объектов Delta как обязательные для каждого разработчика Qt. Это отличный материал для чтения.

Насколько я знаю, это в основном проблема, если объекты существуют в разных streamах. Или, может быть, пока вы на самом деле обрабатываете сигналы.

В противном случае удаление QObject сначала отключит все сигналы и слоты и удалит все ожидающие события события. Как вызов disconnect ().

Давайте будем гением компьютера.