在使用Qt的时候,你是否遇到过这样的场景,从外部传来一个QObject的指针,当使用这个指针的时候,害怕它已经被释放了?如果我们在一个对象A中引用了另一个对象B,当对象B被析构的时候,A对象其实是不知道B已经被析构。这个时候再使用B的话就会导致崩溃。参考如下代码
class A : public QObject
{
Q_OBJECT
public:
A(): m_widget(nullptr) { }
void setWidget(QWidget *w)
{
if (w == nullptr)
return;
m_widget = w;
}
void showWidget()
{
if (m_widget == nullptr)
return;
m_widget->show();
}
private:
QWidget *m_widget;
};
如果m_widget在其他地方被释放掉了,再调用showWidget就是导致崩溃,因为m_widget == nullptr肯定是false。要解决这个问题有2种方式:
- 连接destroyed信号,把m_widget重新设置成nullptr。setWidget的代码如下:
void setWidget(QWidget *w)
{
if (w == nullptr)
return;
m_widget = w;
connect(m_widget, &QWidget::destroyed, this, [this]{
m_widget = nullptr;
});
}
- 使用QPointer,QPointer可以看作是一个智能指针类型。QPointer类是一个模板类,提供指向QObject的受保护指针。需要注意的是模板类型必须是QObject或QObject的派生类,并且QPointer对象在析构的时候不会去释放受保护的指针。因为重载了T *operator->() const,所以我们可像使用普通指针一样使用。修改之后的class A代码如下:
class A : public QObject
{
Q_OBJECT
public:
A(){ }
void setWidget(QWidget *w)
{
m_widget = w;
}
void showWidget()
{
if (m_widget) {
m_widget->show();
} else {
qDebug() << "The widget has been destroyed";
}
}
private:
QPointer<QWidget> m_widget;
};
测试代码:
QWidget *w = new QWidget;
A *a = new A;
a->setWidget(w);
a->showWidget();
delete w;
a->showWidget();
第一次调用showWidget的时候,由于w没有被释放,所以可以显示正常调用。当第二次调用showWidget的时候,w已经被释放了,所以会打印出"The widget has been destroyed"。