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


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

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

delete obj1; delete obj2;

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

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

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

Итак, допустим, я звоню:

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

на всякий случай.

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

4 68

4 ответа:

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

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

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

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

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

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

С ~QObject,

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

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

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

вы можете найти ответ на свой вопрос прочитав о Delta Object Rules которая гласит так:

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

фрагмент:

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

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

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

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

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