Qt中delete的问题
最近项目遇到了一个bug,压力测试ui总会崩溃,gdb调试未果,跑到了库函数,无从查起:
(gdb)bt
#0 0x4146b1e4 in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&,QPoint const&, int, QPainter*, QWidgetBackingStore*) () from/qt/lib/libQt5Widgets.so.5
#1 0x4146bb70 in QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*,QList<QObject*> const&, int, QRegion const&, QPoint const&,int, QPainter*, QWidgetBackingStore*) ()
from /qt/lib/libQt5Widgets.so.5
#2 0x4146b22c in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&,QPoint const&, int, QPainter*, QWidgetBackingStore*) () from/qt/lib/libQt5Widgets.so.5
#3 0x41443918 in QWidgetBackingStore::sync() () from /qt/lib/libQt5Widgets.so.5
#4 0x41462c9c in QWidgetPrivate::syncBackingStore() () from/qt/lib/libQt5Widgets.so.5
#5 0x4146de64 in QWidget::event(QEvent*) () from /qt/lib/libQt5Widgets.so.5
#6 0x41436a00 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from/qt/lib/libQt5Widgets.so.5
#7 0x4143a590 in QApplication::notify(QObject*, QEvent*) () from/qt/lib/libQt5Widgets.so.5
#8 0x00922918 in ?? ()
#9 0x00922918 in ?? ()
Backtracestopped: previous frame identical to this frame (corrupt stack?)
(gdb)
后来发现问题是qt中使用delete出错,
if(p){
delete p; // 修改为: p->deletelater();
p = NULL;// 这里必须置为NULL,否则依然会出错(delete后置空是一个非常好的习惯)
}
查阅的参考资料如下:http://blog.csdn.net/dbzhang800/article/details/6300025、http://my.oschina.net/u/566875/blog/306299
摘录其中一文片段如下:
QT有一套回收内存的机制,主要的规则如下:
1、所有继承自QOBJECT类的类,如果在new的时候指定了父亲,那么它的清理时在父亲被delete的时候delete的,所以如果一个程序中,所有的QOBJECT类都指定了父亲,那么他们是会一级级的在最上面的父亲清理时被清理,而不用自己清理;(注:这里是在new出来对象的时候,这样依靠QObject释放的内存一点问题没有,如果以下这种起情况,那么程序会崩溃。
QObject derver; QObject base; derver.setParent(&base);
儿子和父亲都是栈空间上,释放内存时候,先释放父亲,释放父亲空间的时候,已经把儿子的内存释放了,当释放儿子的内存的是否,会出现内存错误,因为儿子被释放了两次)
2、程序通常最上层会有一个根的QOBJECT,就是放在setCentralWidget()中的那个QOBJECT,这个QOBJECT在 new的时候不必指定它的父亲,因为这个语句将设定它的父亲为总的QAPPLICATION,当整个QAPPLICATION没有时它就自动清理,所以也无需清理。
3、如果我们自己释放掉设置QObject为自己父亲的类对象,那个指向的父亲的QObject会从自己的儿子列表中把这个儿子删除掉,就不会出现儿子的内存会被释放两次,如果我们要删除有这种关系的QObject类的话,正常情况,这样它会将这个儿子移出它的列表,并且重新构建显示内容,但是直接这样做时有风险的!
4、当一个QOBJECT正在接收事件队列时如果中途被你DELETE掉了,就会出现问题了,所以QT中建议大家不要直接DELETE掉一个 QOBJECT,如果一定要这样做,要使用QOBJECT的deleteLater()函数,它会让所有事件都发送完一切处理好后马上清除这片内存,而且就算调用多次的deletelater也不会有问题。
5、QT不建议在一个QOBJECT 的父亲的范围之外持有对这个QOBJECT的指针,因为如果这样外面的指针很可能不会察觉这个QOBJECT被释放,会出现错误,如果一定要这样,就要记住你在哪这样做了,然后抓住那个被你违规使用的QOBJECT的destroyed()信号,当它没有时赶快置零你的外部指针。当然我认为这样做是及其麻烦也不符合高效率编程规范的,所以如果要这样在外部持有QOBJECT的指针,建议使用引用或者用智能指针,如QT就提供了智能指针针对这些情况,见最后一条。
6、QT中的智能指针封装为QPointer类,所有QOBJECT的子类都可以用这个智能指针来包装,很多用法与普通指针一样
(某些内存泄露的检测工具会认为QT的程序因为这种方式存在内存泄露,大家不必理会)
还有一个内存泄漏的问题:
class ReadoutDisplay : public QWidget { Q_OBJECT public: ...... private: QGridLayout *m_Gridlayout; int m_no_of_rows; int m_no_of_col; }; ReadoutDisplay::ReadoutDisplay(QWidget *parent, QString type) : QWidget(parent){ m_type = type; m_Gridlayout = new QGridLayout;
.......
}
泄漏的原因是这里在类中声明的了一个类对象指针,在成员函数中分配了空间,但是没有在成员函数中释放,解决的办法是在析构函数中检查释放:
ReadoutDisplay::~ReadoutDisplay() { if(m_Gridlayout) { delete m_Gridlayout; m_Gridlayout = NULL; // once we delete a pointer, it was highly recommended } }