У меня сейчас что-то вроде этого
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
если окно было закрыто?
Общий указатель не знает, когда вы удаляете объект, на который он указывает. это ошибка вручную удалить объект, срок жизни которого управляется общим указателем. Поскольку окно само удаляется, когда оно закрывается, теперь вы получаете висячие общие указатели.
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. Вы можете использовать если (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’s 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: новый QMainWindow (p). Хорошо, если p не нуль, тогда p удалит cV, когда p удален. Поэтому нет необходимости удалять cV в деструкторе, даже если вы используете QPointer.