QSharedPointer не возвращает false

У меня сейчас что-то вроде этого

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.

Мой вопрос, когда я закрылcVQMainWindow Объект (нажав на кнопку x) почему следующий оператор возвращает true

if(cV)

Как я могу вернуть его false если окно было закрыто?

2 ответа

Общий указатель не знает, когда вы удаляете объект, на который он указывает. Ошибка ручного удаления объекта, временем жизни которого управляет общий указатель. Поскольку окно само удаляется, когда оно закрывается, теперь вы получаете висячие общие указатели.

Ergo, вы не можете использовать QSharedPointer с виджетом, который Qt::WA_DeleteOnClose,

Вам нужен указатель, который отслеживает, существует ли виджет еще. Такой указатель есть QPointer, он делает именно то, что вам нужно. Этот указатель предназначен для сброса себя в ноль, когда QObject разрушается.

Обратите внимание, что 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; }
};

Поскольку мы разрешаем удаление объекта с помощью других средств, кроме указателя, доступ к объекту из нескольких потоков является ошибкой. Если объект должен быть удален в другом потоке, существует условие гонки между нулевой проверкой и использованием разыменованного объекта. Таким образом, QPointerили ScopedObjectPointer может использоваться только из потока объекта. Это прямо заявлено. Q_ASSERT становится неактивным в сборках релизов и не оказывает никакого влияния на производительность.

Объект, защищенный QSharedPointer, предназначен для удаления самим QSharedPointer, когда все владельцы выходят из области видимости. В вашем случае вы позволяете QMainWindow удалять cV, когда пользователь закрывает его. QSharedPointer не знает об этом инциденте и не установит указатель на 0 автоматически.

Ваше решение простое. Забудьте о QSharedPointer и просто используйте QPointer. QPointer автоматически установит указатель на 0, когда объект будет удален вами или QObject. Затем вы можете использовать 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, когда он выходит из области видимости, вы можете использовать ScopedQObjectPointer KubarOber в другом ответе на этот вопрос:

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 удалит cV, когда p удален. Поэтому нет необходимости удалять cV в деструкторе, даже если вы используете QPointer.

Другие вопросы по тегам