这几天一直在模仿QQ做一个即时通讯软件,过程不是很顺利,表现在窗口关闭,应用程序依旧存在,应用程序异常结束,关闭子窗口,主窗口跟着关闭,所以总结了一些内容,方便日后获取。
如果对这个项目有兴趣的话可以前往GitHub:https://github.com/FdogMain/FdogInstantMessaging
main函数中,在栈上创建的窗口,关闭的时候自动调用析构函数,这种情况不可以使用this->setAttribute(Qt::WA_DeleteOnClose);,关闭时会出现异常,如果是在堆上创建,则可以使用this->setAttribute(Qt::WA_DeleteOnClose),调用析构函数时,不会有异常。
当关闭窗口时会调用close函数,这个函数发送一个关闭事件 QCloseEvent,接着窗口将会被隐藏,如果想实现关闭时进行询问,可以拦截QCloseEvent事件,也就是重写QCloseEvent,可以让用户来选择关闭,还是取消。
在不加Qt::WA_DeleteOnClose时,选择关闭,窗口将会消失,其实调用了hide,如果加了之后,除了调用hide,还会调用deleteLater方法来将窗口释放掉,在不加这个属性的情况下,close和hide,还有servisibel的功能是一样,只是会隐藏窗口对象而已,不会销毁对象。
在main,栈上面创建一个窗口A,关闭窗口A时,会调用析构函数。
如果在这个窗口A的构造函数中再创建一个窗口B,并且在A的析构函数中对B进行释放。
第一种形式:
MainWindow * b = new MainWindow();
当关闭窗口A,再关闭窗口B时,创建B的析构函数被调用,窗口A的析构函数被调用 (这种关闭方式有明显的卡顿,当关闭A,按照规则,B应该被关掉,释放,但是B窗口还显示在桌面,多次运行,发现还会存在A析构不执行的问题(析构中的打印语句并未被打印在控制台),所以这种方式存在问题)
反过来,当先关闭窗口B,再关闭窗口A,B的析构函数被调用,窗口A的析构函数被调用 (这种关闭方式无卡顿,实际上是B窗口被隐藏,并未主动执行析构,而在A的析构函数中被动执行,这也是为什么关闭B时,显示并未调用B析构,而关闭A时,才显示调用B析构的原因)
我们给窗口B添加Qt::WA_DeleteOnClose试一下(构造函数中添加setAttribute(Qt::WA_DeleteOnClose)),因为窗口B是窗口在堆上的,可以使用setAttribute(Qt::WA_DeleteOnClose)),
还是先关闭窗口A,再关闭窗口B,显示调用了窗口B的析构函数,然后出现异常,这个异常应该是重复析构B发送的异常。
把窗口A中关于窗口B释放的代码去掉,显示调用了窗口B的析构函数,调用窗口A的析构函数,但是没有出现异常(存在卡顿,多次运行,发现还会存在A析构不执行的问题(析构中的打印语句并未被打印在控制台))。
现在反过来,先关闭窗口B,由于窗口B设置了setAttribute(Qt::WA_DeleteOnClose))属性,立即执行了析构函数,接着关闭窗口A,如果不出意外的话,应该会出现异常,因为窗口B已经被释放,再在窗口A中再次释放B会报异常,把A中析构函数中的释放B的代码再次注释,运行,显示依次调用了窗口B的析构函数,窗口A的析构函数(无卡顿)。
第二种形式,指定父窗口
MainWindow * b = new MainWindow(this);
A窗口析构没有写释放B窗口的代码情况下:
关闭A窗口(被释放),B窗口跟着关闭(被释放)(无卡顿)。
关闭B窗口(只是隐藏),关闭A窗口(被释放)(无卡顿)。
再次试着给 b 添加setAttribute(Qt::WA_DeleteOnClose))属性,关闭B窗口,执行B析构,再关闭A,执行A的析构(无卡顿)。
关闭A窗口,执行A的析构,执行B的析构,并且B窗口被关闭(无卡顿)。
这里是因为QT的父子对象机制在起作用,原因就在于那个this。
当我们使用父对象来创建一个对象的时候 ,父对象会把这个对象添加到自己的子对象列表中。当这个父对象被删除的时候,它会遍历它的子对象类表并且删除每一个子对象,然后子对象们自己再删除它们自己的子对象,这样递归调用直到所有对象都被删除,所以如果new出来的控件,如果有指定父对象,无需我们手动删除。
还有一个发现,就是其他控件如果指定A窗口作为父窗口,是会被嵌入在A窗口中的,但是MainWindow这个类的窗口不会被嵌入
反过来则不然。
但是上面这些仅仅是在基本情况下,当我把窗口属性设置为无边框,无任务栏之后等等不同属性之后,再次关闭窗口,析构函数不会被自动调用,换句话说就是只是窗口关闭了,但是应用程序本身还没有关闭,最明显的特征就是当你关闭了窗口,qt的应用程序输出窗口还是显示着红色的方块而不是绿色的三角。
这个时候可以在你想要关闭的地方添加下列代码,应用程序就会被关闭。
QApplication* app;
app->quit();
还有一种情况就是在MainWindow中创建widget窗口,但是一关闭最后一个widget,MainWindow就会被关闭,是不是不可思议,在没找到解决方案之前,我只能判断是不是最后一个widget,如果是我就隐藏,而不是关闭,举个例子。
例如qq 主界面是MainWindow 双击好友生成widget窗口。
当关闭这些widget窗口到最后一个的时候,主窗口会跟着关闭,出现这种症状的原因之一,是设置了窗口的属性
也就是使用了这个函数setWindowFlags(),一不做二不休,将这个widget窗口再添加一个属性setWindowFlag(Qt::CoverWindow);
这个问题可能会不存在了~
还有一个问题就是关闭窗口,可能会报程序异常结束,这个错误也和main中窗口创建的位置有关。