Qt QObject Parent‑Child Ownership: Safe Memory Management and Thread‑Affinity Gotchas
Learn how Qt’s QObject parent‑child mechanism automates deletion, why thread affinity matters, and how to avoid common pitfalls in multithreaded code.
08 Oct 2025, 12:19 UTC

Problem: Forgetting to delete Qt objects leads to leaks or crashes
In a typical Qt application you create many short‑lived objects—timers, network replies, dialogs—while the lifetime of their owners (windows, controllers, etc.) is longer. Manually calling delete for each child is error‑prone: a missed call leaks memory, and deleting an object that is still owned by another part of the code can cause a double‑free crash.
How parent‑child ownership works
When you construct a The following snippet shows a When the A child inherits the thread affinity of its parent at the moment it is added to the child list. Consequently, if you move the parent to another thread with The parent‑child bookkeeping structures are not protected by a mutex. Creating a Mixing parent‑child ownership with To confirm that the mechanism behaves as described, you can:
These steps rely on Qt’s documented behavior and do not require any special version‑specific features; they work with Qt 5.15 LTS and Qt 6.x. Use Qt’s parent‑child ownership as the default lifetime strategy for objects that share a clear containment hierarchy. Remember that the child follows the parent’s thread, so keep thread moves confined to the parent’s thread or use queued calls for cross‑thread changes. Protect any external raw pointers with QObject with a non‑null parent pointer, Qt automatically adds the new object to the parent’s internal child list. The parent’s destructor iterates that list and calls delete on each child, so the child is destroyed exactly when its parent goes out of scope. Ownership can also be transferred later with setParent(), but a QObject can have only one parent at a time. This mechanism works for widgets, windows, timers, network objects, and most QObject
Worked example: a timer owned by a widget
MainWindow that creates a QTimer and passes this as the parent. No explicit delete for the timer is needed.// mainwindow.cpp
#include
#include
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr)
: QMainWindow(parent), timer(new QTimer(this)) {
connect(timer, &QTimer::timeout, this, &MainWindow::onTick);
timer->start(1000);
}
private:
QTimer *timer;
void onTick() { /* slot implementation */ }
};
MainWindow instance is destroyed (e.g., the user closes the window), Qt’s parent‑child mechanism invokes the timer’s destructor automatically. If you run the program under AddressSanitizer or step through with a debugger, you will see the timer’s destructor executed exactly once, with no explicit delete call in your code.Thread‑affinity caveat
moveToThread(), all of its children are moved as well. This behavior is useful for GUI objects that must stay in the GUI thread, but it can surprise developers who expect a child to remain in its original thread after reparenting.Limitation: not thread‑safe for reparenting
QObject, calling setParent(), or destroying a parent from multiple threads without external synchronization can corrupt the child list, leading to use‑after‑free or double‑delete bugs. To mitigate this risk:
QMetaObject::invokeMethod with Qt::QueuedConnection) to the owner’s thread.QPointer so they automatically become null when the child is deleted.std::unique_ptr or manual delete on the same object will result in a double free; choose one ownership model per object.Practical verification
QObject, allocates a child with new Child(&parent), and destroys the parent.QPointer to the child, reparent the child to nullptr before destroying the parent, and verify that the pointer becomes null and the parent’s destructor does not attempt to delete the child again.Actionable closing
QPointer and avoid mixing this model with manual delete or smart pointers. By following these guidelines you reduce boilerplate, eliminate many memory‑management bugs, and keep your Qt code both safe and readable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.