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

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

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

delete obj1; delete obj2;

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

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

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

Итак, позвольте сказать:

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

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

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

Ответ 1

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

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

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

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

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

Ответ 2

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

Из ~ QObject,

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

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

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

Ответ 3

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

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

Fragment:

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

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

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

Ответ 4

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

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