Diagnosing 'Object Deleted While Still in Use' Crashes in Qt
Learn how to diagnose and fix 'Object deleted while still in use' crashes in Qt by managing the QObject parent-child hierarchy and using QPointer and deleteLater().
08 Jun 2026, 10:33 UTC

The Problem: Use-After-Free in the QObject Hierarchy
A common failure mode in Qt applications is the segmentation fault occurring during event processing or signal emission. This usually happens when a QObject is destroyed, but a raw pointer to that object remains active elsewhere in the application. Because Qt uses a parent-child ownership model, objects are often deleted automatically, leaving the developer with a "dangling pointer"—a reference to a memory location that no longer holds a valid object.
Quick Diagnostic Table
| Symptom | Likely Cause | Primary Suspect |
|---|---|---|
Crash inside QMetaObject::activate |
Slot called on deleted object | Raw pointer in a signal-slot connection |
| Crash during destructor of a parent object | Double-free error | Mixing std::unique_ptr with QObject parenting |
Crash immediately after delete object; |
Self-destruction during event | Calling delete inside a slot |
| Intermittent crash on UI interaction | Dangling pointer access | Child object deleted by parent, but referenced by a member variable |
Step-by-Step Investigation
-
Enable AddressSanitizer (ASan):
Standard debuggers often show the crash site, but not where the memory was freed. Add the following flags to your
.profile (qmake) orCMakeLists.txtto detect use-after-free errors in real-time.# For CMake set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") set(CMAKE_LD_FLAGS "${CMAKE_LD_FLAGS} -fsanitize=address")Risk: ASan increases memory usage and slows down execution. Run this only in a dedicated debug build.
-
Trace the Ownership Chain:
Check if the object is being deleted by a parent. In your debugger, inspect the
parent()property of the crashing object. If the parent is destroyed first, all children are automatically deleted via theQObjectdestructor. -
Audit Smart Pointer Usage:
Search for
std::unique_ptrorstd::shared_ptrwrappingQObjectsthat also have a parent assigned. If you callsetParent(parent)on an object managed by aunique_ptr, both the smart pointer and the parent will attempt to delete the object, causing a double-free crash.
Fixes Based on Findings
Finding: Crash during slot execution
If an object must delete itself (e.g., a dialog closing and cleaning up), never use the delete keyword. This destroys the object while the event loop is still processing a function call on that object's stack.
Fix: Use deleteLater(). This schedules the object for deletion once it returns to the event loop and no more events are pending for that object.
// Incorrect: Causes immediate crash if called from a slot
delete this;
// Correct: Safe deferred deletion
this->deleteLater();
Finding: Dangling pointers to children
If you maintain a pointer to a child object that might be deleted by its parent, a raw pointer will not be nullified when the child is destroyed.
Fix: Replace the raw pointer with QPointer<T>. This is a guarded pointer that automatically resets to nullptr when the referenced QObject is destroyed.
// Header
QPointer<QPushButton> m_submitButton;
// Implementation
if (m_submitButton) {
m_submitButton->setText("Processing...");
} else {
// Object was already deleted by its parent or deleteLater()
}
Finding: Stack-allocated parents
If you create a QObject on the stack and assign it as a parent to a heap-allocated object, the stack object's destructor will attempt to delete the heap object. If that heap object is also managed by a smart pointer, a crash occurs.
Fix: Ensure that if an object has a QObject parent, it is allocated on the heap using new and is not managed by std::unique_ptr.
Verification and Rollback
To verify the fix, run the application under ASan and trigger the specific sequence of events that led to the crash. A successful fix will show no "heap-use-after-free" reports in the console output.
Rollback: If QPointer introduces unexpected null-checks that break logic, revert to raw pointers and implement a manual destroyed() signal connection to nullify the pointer: connect(object, &QObject::destroyed, [this](){ m_ptr = nullptr; });
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.