以前研究过Qt内存回收的机制,现在做一下总结: 总结的说法是:所以继承与QObject的类,并设置了parent(在构造时,或用setParent函数,或parent的addChild相关信息),所以在parent被delete时,这个parent的相关所有child都会自动delete,不用用户手动处理。 根据上面的说法,会出现三个问题: 1.当child被delete时parent怎么知道? 2.parent怎么区分它的child是new出来的,还是不是new的。 3.当parent被delete后,child怎么知道自己被delete了。 我们慢慢来回答: 第一个问题,parent是用一个数给来保存childs的指针的,当一个child被销毁时,child的析构函数会调用parent并把parent的指针数据中自己对数的值改为0,那么最后是0的指不管多少次都无所谓了。 第二个问题,parent还真TMD的不区别它的child是不是new出来的,只要是它的child,它在销毁时就直接delete才不管你是不是new出来的,这时就体现出了delete的强大,delete可以释放掉任何的对象(调用它的析构函数)和内存。注意:这么操作存在一个很大的问题。 第三个问题,child还真不知道它自己是否被delete掉了。野指针出现了吧。 最后我们还要考虑,第二个问题和第三个问题的处理方法: 当delete出一个不是new出来的对象后,编译器可不认帐,不会认为这个对象已经被销毁了,在这个对象的生命周期结束时,程序还会再调用一次这个类的析构函数,出错了吧,一个美好的段错误。为了防止这种情况的出现,我们要防止在非new的child对象结束使用之前销毁parent。在正确的QT开发中,顶级的patent一般是在main函数中,而patent生命周期一般都会比child长,所以正常都不会出错,只要我们编码注意不要写出如下代码: { QObject*parent=newQObject(0); QObjectchild(parent); delete parent; } 上面代码中parent的生命周期比child短,当然如果你代码中有一个child是static的,那我也救不了你。 第三个问题,Qt不建议在一个parent的范围之外持有对childs的指针,按它说的当然不会出野指针的问题了。但是非要在parent外持有child的指针,那么Qt推荐使用QPointer,QPointer相当于一个智能指针,不用智能指针前的代码如下: { QObject*parent=newQObject(0); QObject*child=newQObject(parent); deleteparent; child->... } 第四步操作就会同错了,如果用了QPointer的代码如下 { QObject*parent=newQObject(0); QObject*child=newQObject(parent); QPointer<QObject>p=child; deleteparent; if(p.isNull()){ p->...
} 在使用之前判断是否为空,这时就不会再出错段错误了。 最后我们大致来说一下QPointer的现实原理,在QPointer保存了一个QObject的指针,并把这个指针的指针(双指针)交给全局变量 GuardHash 管理,而QObject 在销毁时(析构函数,QWidget是通过自己的析构函数的,而不是依赖QObject的)会调用QObjectPrivate::clearGuards 函数来把全局 GuardHash 的那个双指针置为*零,因为是双指针的问题,所以QPointer中指针当然也为零了。用isNull 判断就为空了。 Qt这块东西实现的还真是有点复杂啊。但给我们开发中内存管理这一块省了不少麻烦。 最后补充一个问题:当一个QOBJECT正在接受事件队列时如果中途被你销毁掉了,就是出现问题了,所以QT中建议大家不要直接DELETE掉一个QOBJECT,如果一定要这样做,要使用QOBJECT的deleteLater()函数,它会让所有事件都发送完一切处理好后马上清除这片内存,而且就算调用多次的deletelater也不会有问题。 |
|
来自: just_person > 《QT》