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将会自动从windowchildren()列表中删除,然后析构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 能够连接成链,就可以保证每一个组件被正确释放。