題記: 要知道,並不是只有初學者才會犯錯。(shiroki的至理名言)
最近發現了一些有意思的問題,值得memo一下。
先來看段代碼:
#include <QApplication>#include <QWebView>#include <QUrl>int main(int argc, char* argv[]){ QApplication a(argc, argv); QWebView* mw = new QWebView; mw->show(); mw->load(QUrl("http://www.cuteqt.com/blog")); return a.exec();}
大家看得出這段代碼中的問題嗎? (呵呵,不要告訴我是cuteqt不能訪問哦~)
這段代碼ms十分標準, 非常符合筆者平時寫Qt程式書寫main函數的習慣, 孰料想竟然是個錯誤的習慣,而且問題很嚴重哦。 給個提示:在程式退出時會aborted。
如果還沒想出來是什麼問題,嘿嘿,沒關係,看了下面的答案你就明白了。
在這段程式裡QApplication執行個體建立在stack上,生命期是main的大括弧內, 而mw則通過new建立在heap上, 在程式退出時才會被析構。 換句話說,mw的生存期長於application的生存期…..這可是Qt編程的大忌, 因為在Qt中所有的Paint Device都必須要在有QApplication執行個體的情況下建立和使用。 不過如果把這個程式寫出來運行一下, 未必會出現我說的aborted的問題, 大多數代碼類似的程式都能安全的運行(這也是為什麼用了那麼多年的Qt從來沒有注意過這個問題, 並且養成了我錯誤的編程習慣。)。 這裡的trick在於application退出時mw已經被關閉, mw中的所有Paint Device一般都不會被訪問到了, 所以這個錯誤隱藏在很深的陰暗角落, 偷偷地嘲笑我們呢!
要想實驗這個問題也很簡單,把load的參數換成本地檔案 test.html, 並把下面的內容寫進test.html就能看到拉:
-----test.html-----<form><select id="headertest"><option>Item1</option><option>Item2</option><option>Item3</option></select></form>
這個html裡使用了下拉選單。 如果你運行程式並點開該選單,之後退出程式你就會看到Aborted錯誤提示,並列印出錯誤資訊:“QWidget: Must construct a QApplication before a QPaintDevice”。
既然提出的問題,當然也要給出解決的方案。 有兩種可行的方法避免該錯誤。 一個當然是糾正一下編程習慣,對mw不要用new的方式建立,改在stack上建立,如下代碼:
#include <QApplication>#include <QWebView>#include <QUrl>int main(int arg, char* argv[]){ QApplication a(argc, argv); QWebView mw; mw.show(); mw.load(QUrl("http://www.cuteqt.com/blog")); return a.exec();}
另外還可以用Qt提供的API解決此問題, 想辦法讓mw在application之前clean up, 那就是用WA_DeleteOnClose屬性。 該屬性標示表單會在close時被析構, 這樣就保證不會留存在application析構之後了, 是個很好的辦法。
代碼如下:
#include <QApplication>#include <QWebView>#include <QUrl>int main(int arg, char* argv[]){ QApplication a(argc, argv); QWebView* mw = new QWebView; mw->show(); mw->setAttribute(Qt::WA_DeleteOnClose); mw->load(QUrl("http://www.cuteqt.com/blog")); return a.exec();}