QObject三大核心功能——内存管理

2021-05-30  本文已影响0人  上官宏竹

信号与槽,内存管理,事件处理

QObject的parent or 对象树(Object Tree)

  1. QObject构造函数中,对于对象树处理的流程:如果指定了parent,那么先判断parent和当前对象是否在同一线程,如果不在就令parent=0,如果当前对象是widget且存在parent(在同一线程),那么赋给parent指针,而且后者添加本对象到子对象的QList,在QWidget构造函数最后发送事件QEvent::ChildAdded。
    如果当前对象不是widget就调用setParnet,又调用了QObjectPrivate::setParent_helper。参考
  2. 对于在局部作用域上创建的父对象及其子对象,要注意对象销毁的顺序,因为父对象销毁时也会销毁子对象,当子对象会在父对象之后被销毁时会引发double free。
  3. 如果已经指定了非NULL的parent,这时将它设置成了NULL,那么当前实例会从父对象的children中删除,不再受到QObject & parent机制的影响;
  4. 对于QObject及其派生类,如果彼此之间存在一定联系,则应该尽量指定parent,对于QWidget应该指定parent或者加入布局管理器由管理器自动设置parent。
    对象树(Object Tree)

WA_DeleteOnClose

  1. qwidget关闭的流程(默认是隐藏,而未释放内存),首先用户触发close()槽,然后Qt向widget发送QCloseEvent,默认的QCloseEvent会做如下处理:
    将widget隐藏,也就是hide()
    如果有设置Qt::WA_DeleteOnClose,那么会接着调用widget的析构函数
  2. close机制导致的内存泄漏的例子:因为dialog在close时会被隐藏,而且没有设置DeleteOnClose,所以Qt不会去释放dialog,而用户也无法回收dialog的资源。
button.ConnectClicked(func (_ bool) {
  dialog := NewMyDialog()
  dialog.Exec()
})

解决办法也有三个:
第一种是使用deleteLater,例如:dialog.DeleteLater()。这会通知Qt的eventloop在下次进入主循环的时候析构dialog,这样一来确实解决了内存泄露,不过缺点是会有不可预测的延迟存在,有时候延迟是难以接受的。
第二种是手动删除widget,适用于parent为NULL的场合:delete dialog;
第三种比较简单,对于单纯显示而不需要和父控件做交互的widget,直接设置DeleteOnClose即可,close时widget会被自动析构。

QObject::deleteLater()

当一个QObject正在接受事件队列时如果中途被你delete掉了,就是出现问题了,所以QT中建议大家不要直接delete掉一个QObject,如果一定要这样做,要使用QObject的deleteLater()函数,它会让所有事件都发送完一切处理好后马上清除这片内存,而且就算调用多次的deleteLater()也不会有问题。
deleteLater源码如下,从源码可以看到,返回到事件循环后,调用deleteLater()的对象才会被销毁,否则不执行。如果线程中没有运行着的事件循环,线程中的对象调用了deleteLater(),当线程结束后对象才被销毁。当第一次QDeferredDeleteEvent传递给事件循环后,对象的任何待处理事件(pending events)都从事件队列中移除。

void QObject::deleteLater()
{
    QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}

bool QObject::event(QEvent *e)
{
    switch (e->type()) {
    ......
    case QEvent::DeferredDelete:
        qDeleteInEventHandler(this);
        break;
    }
}

void qDeleteInEventHandler(QObject *o)
{
    delete o;
}
上一篇下一篇

猜你喜欢

热点阅读