QT中父子QObject双向持有QPointer场景的崩溃问题咨询
问题1 为什么设置父对象的同时还要用QPointer持有父对象引用?
- 省去类型转换成本:
QObject::parent()返回的是通用QObject*类型,如果要直接使用QParent类的自定义成员/方法,每次调用都要做强制类型转换,额外存QPointer<QParent>可以直接用,减少冗余代码。 - 避免野指针访问:存在部分场景会打破默认父子对象生命周期绑定逻辑,比如手动调用
QChild::setParent()更换父对象、外部代码提前删除QParent等,QPointer是Qt提供的监护指针,指向的QObject被销毁时会自动置空,使用前判空即可避免访问非法地址,比裸指针安全。 - 适配异步场景:如果
QChild内有异步触发的槽函数、定时器回调等逻辑,执行回调时QParent可能已经被销毁,用QPointer可以提前做有效性校验,规避异步访问异常。
问题2 是否存在访问已析构QParent的风险?
存在,风险来自Qt父子对象的析构顺序:
QParent析构时会先执行自身派生类的析构逻辑,销毁QParent自定义成员、释放派生类占用的资源,此时QObject基类还未开始析构,所有指向它的QPointer不会被置空。- 接下来
QObject基类析构的第一步就是销毁所有子对象,触发QChild的析构函数,如果此时QChild析构函数中通过m_parent访问QParent的自定义成员、虚函数,就会访问已经被析构的无效内存,即使m_parent看起来不是空指针。 - 等
QObject基类析构到收尾阶段时,才会把所有关联的QPointer置为空。
问题3 该风险是否是崩溃的诱因?
大概率是,完全匹配描述的崩溃特征:
这种时序窗口导致的非法内存访问本身就具备偶发、难复现的特性,且崩溃发生的时机正好是QParent析构时销毁子对象的流程,和调用栈信息完全吻合。常见的崩溃触发场景就是QChild析构函数中未做有效性校验,直接访问了QParent已经被释放的成员、或者调用了已经被析构的派生类的虚函数。
内容的提问来源于stack exchange,提问作者Ghita
相关产品推荐
相关产品推荐

