1

我目前有这样的东西

QSharedPointer<QMainWindow> cv;

此共享指针用作

cV = QSharedPointer<QMainWindow>(new QMainWindow(p));
cV->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);
cV->show();

现在如果我关闭QMainWindow然后下面的代码使应用程序崩溃

if(cV)
    cV->close(); //This pointer is no longer valid.

我的问题是当我关闭cV QMainWindow对象时(通过单击 x 按钮)为什么以下语句返回 true

if(cV)

false如果窗口已关闭,我怎样才能让它返回?

4

2 回答 2

5

共享指针不会神奇地知道您何时删除它指向的对象。手动删除生命周期由共享指针管理的对象是错误的。由于窗口在关闭时会自行删除,因此您现在会得到悬空的共享指针。

因此,您不能QSharedPointer使用Qt::WA_DeleteOnClose.

您需要的是一个跟踪小部件是否仍然存在的指针。这样的指针是QPointer,它正是你所需要的。QObject该指针旨在当 a被破坏时将自身重置为零。

请注意,这QPointer是一个弱指针,当它超出范围时它不会删除窗口。

如果您需要一个允许删除底层的拥有指针QObject,有一种方法可以做到:

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; }
};

由于我们允许通过指针以外的其他方式删除对象,因此从多个线程访问对象是错误的。如果要在另一个线程中删除对象,则在 null 检查和使用取消引用的对象之间存在竞争条件。因此, aQPointer或 aScopedObjectPointer只能在对象的线程中使用。这是明确断言的。这Q_ASSERT成为发布版本中的无操作,并且在那里没有性能影响。

于 2014-08-14T23:52:05.030 回答
1

当所有所有者超出范围时,由 QSharedPointer 保护的对象将被 QSharedPointer 本身删除。在您的情况下,您让 QMainWindow 在用户关闭它时删除它。QSharedPointer 不知道该事件并且不会自动将指针设置为 0。

您的解决方案很简单。忘记 QSharedPointer,只使用 QPointer。当对象被自己或被 QObject 删除时,QPointer 会自动将指针设置为 0。然后,您可以使用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;
};

如果您想避免在析构函数中删除并在超出范围时自动删除 cV,您可以在此问题的另一个答案中使用 KubarOber 的ScopedQObjectPointer :

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;
};

编辑:我刚刚注意到您在创建 cV 时使用了父级:new QMainWindow(p)。好吧,如果 p 不为空,那么 p 将在 p 被删除时删除 cV。所以即使你使用 QPointer,也不需要在析构函数中删除 cV。

于 2014-08-14T23:56:09.923 回答