Qt中的内存泄露

作者:胡婉莉

    Qt作为一个跨平台的C++开发框架,包含一个功能丰富的C++类库及简便易用的集成开发工具。在C++中,我们都知道new和delete必须一一配对使用,否则会发生内存泄露。但是在Qt我们只看到不停地new,却很少有delete。这样不会发生内存泄露吗?
    Qt有一套自己的回收内存的机制,能够半自动的回收内存。该回收机制的核心是Qt类特有的父子关系。注意不是C++中的基类和派生类的关系。对一个QObject的派生类的对象,可以通过构造函数或者setParent()等函数给它指定一个父对象。同时有些Qt库中的类有默认的父类,例如窗口部件的父类默认是QWidget。QObject及其派生类的对象,如果其parent非0,那么其parent析构时会析构该对象。在Qt中,每个 QObject及其派生类的对象内部都有一个list,用来保存所有的 children,还有一个指针,保存自己的parent。当它自己析构时,它会将自己从parent的列表中删除,并且析构掉所有的children。
      这样听起来似乎很方便,但是使用不当依然会造成内存泄露等问题。下面举例说明一些Qt的内存泄露问题。
      Ex1:

    


    这个例子大家想必都很眼熟,的确,这是C++ GUI Programming with Qt 4 一书的第一个例子。这里Qlabel就是new了没有delete,会发生内存泄露,这个程序比较小,内存泄露影响也不大。这个例子可能只是想培养使用指针建对象的意识。那么如何解决这个问题呢?
        (1)可以将label建在栈上,这样程序结束时能自动析构,就不存在内存泄露问题了。
        (2)可以设置label的父类为QApplication app,由于app是建在栈上的,在程序结束时会自动析构,同时也会将label析构。
        (3)手动delete
        (4) QLabel是QWidget的派生类,可以设置setAttribute() Qt::WA_DeleteOnClose标志位,使它在close后自动delete。
        (5)Qt是事件驱动的,可以将label的closed信号与deletelater槽连接到一起。即:QObject::connect(button, SIGNAL(clicked()),button, SLOT(deleteLater()))
     Ex2:

    


这个例子比较隐晦,但是在程序退出时会崩溃,因为label和w都定义在栈里面,而w比label后定义,所以程序结束时,w会先析构,而由于label是w的子对象,所以内存也会被释放,而label是建在栈上的,所以会引起释放非堆内存。
    所以,Qt虽然能自动回收大部分的内存,但是使用不当仍可能造成内存泄露或者其他更严重的问题。为了避免这个问题,建议首先开发人员需要明白Qt的内存回收机制,然后可以借助缺陷测试工具来测试内存泄露问题。