2013-12-04 1 views
3

Давайте рассмотрим этот простой пример:Безопасно ли излучать сигнал, передавая указатель QObject как параметр прямо перед тем, как переданный объект будет уничтожен?

Class Emitter: public QObject { 
... 
signal: 
    surfaceDestroyed(QObject*); 
public: 
    void emittingMethod(QObject* surface) { 
     emit surfaceDestroyed(surface); 
     delete surface; 
    } 
} 

У меня есть соединение с очередями для этого случая

connect(emitterObject, SIGNAL(surfaceDestroyed(QObject*), receiverObject, 
    SLOT(onSurfaceDestroyed(QObject*)), Qt::QueuedConnection); 

В onSurfaceDestroyed методе получил QObject разыменовывается и используется

Таким образом, вопрос, насколько безопасно этот код? Я прочитал довольно много информации об этом на сайте QT и здесь, но у меня все еще нет четкого понимания проблемы.

На мой взгляд этот код не является безопасным, так как сигнал может быть послан, а затем на поверхность разрушено, чем тогда, когда событие приходит receiverObject после того, как цикл событий обрабатывается и объект-получатель доступов выпустила память, таким образом, SIGSEGV

Это упрощенный пример реального кода, поэтому трудно отслеживать, могут ли из-за этого произойти сбой.

+1

Я думаю, что вызов 'surface-> deleteLater();' будет еще более безопасным. – vahancho

+0

Ну, как правило, если это плохо, не делайте этого. :-) Не было бы лучше, если бы деструктор разрушенного объекта делал то, что вы хотите сделать? –

+0

@ vahancho да, я хотел упомянуть об deleteLater на вопрос, но, в конце концов, это сошло с ума. Как правило, это должно быть безопаснее, но я хотел бы быть на 100% уверенным, если этот код может или не может потерпеть крах. – rightaway717

ответ

5

Это зависит от типа соединения сигнального слота. Соединения - direct по умолчанию, если отправитель и получатель принадлежат к одному и тому же потоку. В этом случае слот будет вызываться немедленно, когда вы излучаете сигнал. Когда сигнальная функция закончена, вы можете быть уверены, что слот также закончен.

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

Итак, если вы хотите написать такой код, убедитесь, что ваши объекты находятся в одном потоке. Вы можете передать Qt::DirectConnection опции функции connect, чтобы сделать тип подключения более понятным.

Если вы хотите использовать подключение в очереди, вы можете издавать, например. aboutToBeDeleted Сигнал в отправителе. Приемник получит этот сигнал, обработает его каким-то образом и ответит сигналом cleanUpCompleted, который вызовет фактическое удаление объекта.

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

+0

@vahancho Я не думаю, что я все еще могу использовать QObject, который испускал destroy() для доступа к этим объектам и т. Д., Так как в то время, когда я его получил, все деструкторы производных классов из QObject уже выполнили свою работу, правильно? destroy() излучается прямо из dtor QObject. – rightaway717

+0

@ rightaway717 Вы можете использовать сигнал 'destroy()' для доступа к объекту, который он отправляет в качестве параметра, если вы используете прямое соединение. Если вы используете очередное соединение, тогда объект будет уничтожен до того, как ваш звонок будет вызван. – thuga

+0

Поверхность @thuga в моем случае является объектом производного класса из QObject, позволяет вызывать этот класс Surface. QObject dtor не является тривиальным, поэтому ~ Поверхность считается не тривиальной ни в любом случае. Согласно стандарту C++ разыменование указателя объекта, чей нетривиальный деструктор был вызван, - UB. Таким образом, я думаю, что даже если у меня есть прямая связь, это все еще UB. Пожалуйста, исправьте меня, если я ошибаюсь. – rightaway717

Смежные вопросы