Reputation: 11671
I currently have something like this
QSharedPointer<QMainWindow> cv;
This shared pointer is used as
cV = QSharedPointer<QMainWindow>(new QMainWindow(p));
cV->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);
cV->show();
Now if I close the the QMainWindow
then the following code makes the app crash
if(cV)
cV->close(); //This pointer is no longer valid.
My Question is when I closed thecV
QMainWindow
Object (by clicking on the x button of the ) why is the following statement returning true
if(cV)
How could i make it return false
if the Window has been closed?
Upvotes: 1
Views: 868
Reputation: 98505
A shared pointer doesn't magically know when you delete the object it points to. It is an error to manually delete an object whose lifetime is managed by a shared pointer. Since the window self-deletes when it gets closed, you now get dangling shared pointers.
Ergo, you can't use QSharedPointer
with a widget that is Qt::WA_DeleteOnClose
.
What you need is a pointer that tracks whether the widget still exists. Such pointer is QPointer
, it does exactly what you need. That pointer is designed to reset itself to zero when a QObject
gets destroyed.
Note that QPointer
is a weak pointer, it won't delete the window when it goes out of scope.
If you need an owning pointer that allows deletion of the underlying QObject
, there's a way to do it:
template <typename T> class ScopedQObjectPointer {
Q_DISABLE_COPY(ScopedQObjectPointer)
QPointer<T> m_ptr;
inline void check() const {
Q_ASSERT(m_ptr && (m_ptr->thread() == 0
|| m_ptr->thread() == QThread::currentThread()));
}
public:
explicit ScopedQObjectPointer(T* obj = 0) : m_ptr(obj) {}
ScopedQObjectPointer(ScopedQObjectPointer &&other) : m_ptr(other.take()) {}
~ScopedQObjectPointer() { check(); delete m_ptr; }
operator T*() const { check(); return m_ptr; }
T & operator*() const { check(); return *m_ptr; }
T * operator->() const { check(); return m_ptr; }
T * data() const { check(); return m_ptr; }
T * take() { check(); T * p = m_ptr; m_ptr.clear(); return p; }
void reset(T * other) { check(); delete m_ptr; m_ptr = other; }
operator bool() const { check(); return m_ptr; }
};
Since we allow the deletion of the object through other means than the pointer, it is an error to access the object from multiple threads. If the object were to be deleted in another thread, there's a race condition between the null check and the use of dereferenced object. Thus, a QPointer
, or a ScopedObjectPointer
can only be used from object's thread. This is explicitly asserted. The Q_ASSERT
becomes a no-op in release builds and has no performance impact there.
Upvotes: 5
Reputation: 3982
The object guarded by QSharedPointer is meant to be deleted by QSharedPointer itself when all owners go out of scope. In your case, you are letting QMainWindow to delete cV when user closes it. QSharedPointer has no knowledge about that incident and will not set the pointer to 0 automatically.
Your solution is simple. Forget about QSharedPointer and just use QPointer. QPointer will automatically set a pointer to 0 when the object is deleted by yourself or by QObject. You can then use if (cV).
class foo {
public:
~foo() {
delete cV; // delete cV if cV is not 0.
}
void showMainWindow() {
if ( ! cV) {
cV = new QMainWindow();
cV->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);
}
cV->show();
}
void closeMainWindow() {
if (cV) { // cV is 0 if it is already deleted when user closes it
cV->close();
}
}
private:
QPointer<QMainWindow> cV;
};
If you want to avoid delete in destructor and automatically deletes cV when it goes out of scope, you can use KubarOber's ScopedQObjectPointer in another answer to this question:
class foo {
public:
~foo() {
}
void showMainWindow() {
if ( ! cV) {
cV.reset(new QMainWindow());
cV->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);
}
cV->show();
}
void closeMainWindow() {
if (cV) { // cV is 0 if it is already deleted when user closes it
cV->close();
}
}
private:
ScopedQObjectPointer<QMainWindow> cV;
};
EDIT: I just noticed that you use a parent when creating cV: new QMainWindow(p). Well if p is not null then p will delete cV when p is deleted. So there's no need to delete cV in destructor even if you use QPointer.
Upvotes: 1