QT中的内存管理
调用析构函数与操作系统主动回收内存的区别
当程序的生命周期结束时,操作系统会直接回收所有程序占用的内存,并且不调用析构函数。这可能导致一些预料之外的情况。
比如强制关闭编辑器会导致编辑器的最近更改消失。
Qt对象树
Qt中的所有可见控件都继承自QObject类。因此,Qt通过QObject类构建了一个对象树,用于方便Qt GUI程序的内存管理。
当创建一个QObject对象时,可以为其制定一个父对象。这时,该QObject对象被添加到父对象的children()列表中。当该QObject对象被析构时,将自动从children()列表中删除自己,而当父对象析构时,其children()列表中的所有子对象都将被析构。
注意:这里的父类和子类不是指继承上的,而是指对象权限:父类可以控制子类的生命周期,而子类只能控制自己的生命周期。
利用Qt的这个特性,可以在结束一个窗口时,只delete父对象的指针。
标准C++对象析构的顺序是:以与声明顺序相反的顺序调用对象的析构函数(堆上的对象的生命周期取决于程序员)。
父对象的指定
实现看下面一段代码:
void f(){
QWidget window;
QPushButton quit("Quit",&window);
}
当该函数结束运行时,按照标准C++的析构顺序,实现析构quit,此时,quit将会自动从window的children()列表中删除,然后析构window,代码没有任何错误。
第二段代码:
void f(){
QPushButton quit("QUit");
QWidget window;
quit.setParent(&window);
}
当该函数结束时,实现调用window的析构函数,此时,window自动调用quit的析构函数。然后C++调用quit的析构函数。也就是说,quit被析构了两次。
因此,为了不再开发中出现意料之外的小毛病,尽量在创建对象时就指定父对象
Q&A
博主你说“在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。”,可是您又在《hello world》中拒了一个在堆上创建QLabel的反例。求解惑。
在 main() 函数中,不应该在堆上面创建对象。这是由于如果在 main() 中在堆上面创建对象,app.exec() 函数是一个死循环,创建出的这个对象没有办法被 delete(不开启事件循环,组件就不能显示,不显示组件就不能 delete,否则你还创建它干什么呢?)。另外的原因是,由于我们的 QApplication 是在栈上面创建的,在堆上面创建的 QLabel 对象生命周期要长于 QApplication,这在 Qt 中是应该避免的。而对于我们自己定义的组件就没有这个问题,因为不在 main() 函数中,我们始终可以保证最晚在关闭时销毁(当然是不发生内存泄露的情况下),也就没有这个问题。
你说的这句话我不是很理解“另外的原因是,由于我们的 QApplication 是在栈上面创建的,在堆上面创建的 QLabel 对象生命周期要长于 QApplication,这在 Qt 中是应该避免的。而对于我们自己定义的组件就没有这个问题,因为不在 main() 函数中,我们始终可以保证最晚在关闭时销毁(当然是不发生内存泄露的情况下),也就没有这个问题。” 为什么我们自己定义的组件不在 main() 函数中,可以解释一下吗? 谢谢!
我们自己定义的组件不在 main() 函数直接被调用,而是通过 MainWindow 之类的顶层窗口调用,而 MainWindow 通常直接被 main() 实例化。因此,只要保证了最顶层的 MainWindow 能够正确释放,并且 parent 能够连接成链,就可以保证每一个组件被正确释放。