15
votes

When a QObject-derived object is being destructed, is it OK to emit a signal from its destructor? I tried it and it seems to work, but I'm not sure if it should be done.

For example, this code

class MyClass : public QObject {
signals:
    void mySignal(const QString &str);
public:
    QString myString;
    ~MyClass() { emit mySignal(myString); }
}

would pass a const reference to an object that might be out of scope by the time when the connected slot is executed.

2

2 Answers

14
votes

Emission is generally fine (QObject does it too with the "destroyed" signal), including a case as yours. When the connection is direct, the string is still alive. And when it is QueuedConnection, then the string is first copied to the event loop.

5
votes

If you ask is it OK: Yes, it will not cause any problem in itself.

If you would ask if its a generally safe thing to do in Qt? Definitely not safe. You have to be very mindful what you do if you emit from destructor, and have a good understanding of the Qt event system.

Remember that when a QObject descendant destructs, it disconnects all signals, so the destructed object does not get any more calls on their slots? Well there is a catch: destruction order. The QObject destructor does that disconnect, and it is the LAST to destruct, meaning, in the destruction chain events might still arrive to the "half-dead" object, causing access violations when accessing virtual functions and members of already destructed descendants. The possibility is present if you use the event system, and any of these conditions are met:

  • In multi threaded environment, if the object is not destructed on its own thread.
  • In multi threaded environment, if the object's destruction chain triggers the run of a processEvents() on any run path.
  • In multi threaded environment, if any object on another thread has a direct connection to this object, and it fails to react to its destroyed signal in direct connection.
  • In single threaded environment, when the destructors sends signals that might return to the object in a direct connect chain.

I call this effect "life during death", and emiting signals or running any form of processEvents() (typically accidentally) in the destructor increase the chance to create such an error.

Of course, if you can somehow guarantee that not any present or future code will actually trigger any slots during destruction, its perfectly safe to emit from destructor, but its very hard to give such guarantee, and I'd advice simply avoid it whenever possible.