简体   繁体   中英

QSharedPointer does not return false

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 the cV 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?

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM