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

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

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

delete obj1; delete obj2;

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

удаление QObject во время ожидающие события, ожидающие доставки, могут вызвать сбой.

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

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

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

для безопасности.

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

4 ответов


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

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

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

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

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


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

С ~QObject,

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

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

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


вы можете найти ответ на свой вопрос прочитав о Дельта-Правила Объектов которая гласит так:

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

фрагмент:

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

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

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


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

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