【Qt笔记】对象模型
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
標(biāo)準(zhǔn) C++ 對(duì)象模型在運(yùn)行時(shí)效率方面卓有成效,但是在某些特定問(wèn)題域下的靜態(tài)特性就顯得捉襟見(jiàn)肘。GUI 界面需要同時(shí)具有運(yùn)行時(shí)的效率以及更高級(jí)別的靈活性。為了解決這一問(wèn)題,Qt “擴(kuò)展”了標(biāo)準(zhǔn) C++。所謂“擴(kuò)展”,實(shí)際是在使用標(biāo)準(zhǔn) C++ 編譯器編譯 Qt 源程序之前,Qt 先使用一個(gè)叫做 moc(Meta Object Compiler,元對(duì)象編譯器)的工具,先對(duì) Qt 源代碼進(jìn)行一次預(yù)處理(注意,這個(gè)預(yù)處理與標(biāo)準(zhǔn) C++ 的預(yù)處理有所不同。Qt 的 moc 預(yù)處理發(fā)生在標(biāo)準(zhǔn) C++ 預(yù)處理器工作之前,并且 Qt 的 moc 預(yù)處理不是遞歸的。),生成標(biāo)準(zhǔn) C++ 源代碼,然后再使用標(biāo)準(zhǔn) C++ 編譯器進(jìn)行編譯。如果你曾經(jīng)為信號(hào)函數(shù)這樣的語(yǔ)法感到奇怪(現(xiàn)在我們已經(jīng)編譯過(guò)一些 Qt 程序,你應(yīng)當(dāng)注意到了,信號(hào)函數(shù)是不需要編寫(xiě)實(shí)現(xiàn)代碼的,那怎么可以通過(guò)標(biāo)準(zhǔn) C++ 的編譯呢?),這其實(shí)就是 moc 進(jìn)行了處理之后的效果。
?
Qt 使用 moc,為標(biāo)準(zhǔn) C++ 增加了一些特性:
- 信號(hào)槽機(jī)制,用于解決對(duì)象之間的通訊,這個(gè)我們已經(jīng)了解過(guò)了,可以認(rèn)為是 Qt 最明顯的特性之一;
- 可查詢(xún),并且可設(shè)計(jì)的對(duì)象屬性;
- 強(qiáng)大的事件機(jī)制以及事件過(guò)濾器;
- 基于上下文的字符串翻譯機(jī)制(國(guó)際化),也就是 tr() 函數(shù),我們簡(jiǎn)單地介紹過(guò);
- 復(fù)雜的定時(shí)器實(shí)現(xiàn),用于在事件驅(qū)動(dòng)的 GUI 中嵌入能夠精確控制的任務(wù)集成;
- 層次化的可查詢(xún)的對(duì)象樹(shù),提供一種自然的方式管理對(duì)象關(guān)系。
- 智能指針(QPointer),在對(duì)象析構(gòu)之后自動(dòng)設(shè)為 0,防止野指針;
- 能夠跨越庫(kù)邊界的動(dòng)態(tài)轉(zhuǎn)換機(jī)制。
通過(guò)繼承QObject類(lèi),我們可以很方便地獲得這些特性。當(dāng)然,這些特性都是由 moc 幫助我們實(shí)現(xiàn)的。moc 其實(shí)實(shí)現(xiàn)的是一個(gè)叫做元對(duì)象系統(tǒng)(meta-object system)的機(jī)制。正如上面所說(shuō),這是一個(gè)標(biāo)準(zhǔn) C++ 的擴(kuò)展,使得標(biāo)準(zhǔn) C++ 更適合于進(jìn)行 GUI 編程。雖然利用模板可以達(dá)到類(lèi)似的效果,但是 Qt 沒(méi)有選擇使用模板。按照 Qt 官方的說(shuō)法,模板雖然是內(nèi)置語(yǔ)言特性,但是其語(yǔ)法實(shí)在是復(fù)雜,并且由于 GUI 是動(dòng)態(tài)的,利用靜態(tài)的模板機(jī)制有時(shí)候很難處理。而自己使用 moc 生成代碼更為靈活,雖然效率有些降低(一個(gè)信號(hào)槽的調(diào)用大約相當(dāng)于四個(gè)模板函數(shù)調(diào)用),不過(guò)在現(xiàn)代計(jì)算機(jī)上,這點(diǎn)性能損耗實(shí)在是可以忽略。
在本節(jié)中,我們將主要介紹 Qt 的對(duì)象樹(shù)。還記得我們前面在MainWindow的例子中看到了 parent 指針嗎?現(xiàn)在我們就來(lái)解釋這個(gè) parent 到底是干什么的。
QObject是以對(duì)象樹(shù)的形式組織起來(lái)的。當(dāng)你創(chuàng)建一個(gè)QObject對(duì)象時(shí),會(huì)看到QObject的構(gòu)造函數(shù)接收一個(gè)QObject指針作為參數(shù),這個(gè)參數(shù)就是 parent,也就是父對(duì)象指針。這相當(dāng)于,在創(chuàng)建QObject對(duì)象時(shí),可以提供一個(gè)其父對(duì)象,我們創(chuàng)建的這個(gè)QObject對(duì)象會(huì)自動(dòng)添加到其父對(duì)象的children()列表。當(dāng)父對(duì)象析構(gòu)的時(shí)候,這個(gè)列表中的所有對(duì)象也會(huì)被析構(gòu)。(注意,這里的父對(duì)象并不是繼承意義上的父類(lèi)!)這種機(jī)制在 GUI 程序設(shè)計(jì)中相當(dāng)有用。例如,一個(gè)按鈕有一個(gè)QShortcut(快捷鍵)對(duì)象作為其子對(duì)象。當(dāng)我們刪除按鈕的時(shí)候,這個(gè)快捷鍵理應(yīng)被刪除。這是合理的。
QWidget是能夠在屏幕上顯示的一切組件的父類(lèi)。QWidget繼承自QObject,因此也繼承了這種對(duì)象樹(shù)關(guān)系。一個(gè)孩子自動(dòng)地成為父組件的一個(gè)子組件。因此,它會(huì)顯示在父組件的坐標(biāo)系統(tǒng)中,被父組件的邊界剪裁。例如,當(dāng)用戶(hù)關(guān)閉一個(gè)對(duì)話框的時(shí)候,應(yīng)用程序?qū)⑵鋭h除,那么,我們希望屬于這個(gè)對(duì)話框的按鈕、圖標(biāo)等應(yīng)該一起被刪除。事實(shí)就是如此,因?yàn)檫@些都是對(duì)話框的子組件。
當(dāng)然,我們也可以自己刪除子對(duì)象,它們會(huì)自動(dòng)從其父對(duì)象列表中刪除。比如,當(dāng)我們刪除了一個(gè)工具欄時(shí),其所在的主窗口會(huì)自動(dòng)將該工具欄從其子對(duì)象列表中刪除,并且自動(dòng)調(diào)整屏幕顯示。
我們可以使用QObject::dumpObjectTree()和QObject::dumpObjectInfo()這兩個(gè)函數(shù)進(jìn)行這方面的調(diào)試。
Qt 引入對(duì)象樹(shù)的概念,在一定程度上解決了內(nèi)存問(wèn)題。
當(dāng)一個(gè)QObject對(duì)象在堆上創(chuàng)建的時(shí)候,Qt 會(huì)同時(shí)為其創(chuàng)建一個(gè)對(duì)象樹(shù)。不過(guò),對(duì)象樹(shù)中對(duì)象的順序是沒(méi)有定義的。這意味著,銷(xiāo)毀這些對(duì)象的順序也是未定義的。Qt 保證的是,任何對(duì)象樹(shù)中的?QObject對(duì)象 delete 的時(shí)候,如果這個(gè)對(duì)象有 parent,則自動(dòng)將其從 parent 的children()列表中刪除;如果有孩子,則自動(dòng) delete 每一個(gè)孩子。Qt 保證沒(méi)有QObject會(huì)被 delete 兩次,這是由析構(gòu)順序決定的。
如果QObject在棧上創(chuàng)建,Qt 保持同樣的行為。正常情況下,這也不會(huì)發(fā)生什么問(wèn)題。來(lái)看下下面的代碼片段:
{QWidget window;QPushButton quit("Quit", &window); }作為父組件的 window 和作為子組件的 quit 都是QObject的子類(lèi)(事實(shí)上,它們都是QWidget的子類(lèi),而QWidget是QObject的子類(lèi))。這段代碼是正確的,quit 的析構(gòu)函數(shù)不會(huì)被調(diào)用兩次,因?yàn)闃?biāo)準(zhǔn) C++ (ISO/IEC 14882:2003)要求,局部對(duì)象的析構(gòu)順序應(yīng)該按照其創(chuàng)建順序的相反過(guò)程。因此,這段代碼在超出作用域時(shí),會(huì)先調(diào)用 quit 的析構(gòu)函數(shù),將其從父對(duì)象 window 的子對(duì)象列表中刪除,然后才會(huì)再調(diào)用 window 的析構(gòu)函數(shù)。
但是,如果我們使用下面的代碼:
{QPushButton quit("Quit");QWidget window;quit.setParent(&window); }情況又有所不同,析構(gòu)順序就有了問(wèn)題。我們看到,在上面的代碼中,作為父對(duì)象的 window 會(huì)首先被析構(gòu),因?yàn)樗亲詈笠粋€(gè)創(chuàng)建的對(duì)象。在析構(gòu)過(guò)程中,它會(huì)調(diào)用子對(duì)象列表中每一個(gè)對(duì)象的析構(gòu)函數(shù),也就是說(shuō), quit 此時(shí)就被析構(gòu)了。然后,代碼繼續(xù)執(zhí)行,在 window 析構(gòu)之后,quit 也會(huì)被析構(gòu),因?yàn)?quit 也是一個(gè)局部變量,在超出作用域的時(shí)候當(dāng)然也需要析構(gòu)。但是,這時(shí)候已經(jīng)是第二次調(diào)用 quit 的析構(gòu)函數(shù)了,C++ 不允許調(diào)用兩次析構(gòu)函數(shù),因此,程序崩潰了。
由此我們看到,Qt 的對(duì)象樹(shù)機(jī)制雖然幫助我們?cè)谝欢ǔ潭壬辖鉀Q了內(nèi)存問(wèn)題,但是也引入了一些值得注意的事情。這些細(xì)節(jié)在今后的開(kāi)發(fā)過(guò)程中很可能時(shí)不時(shí)跳出來(lái)煩擾一下,所以,我們最好從開(kāi)始就養(yǎng)成良好習(xí)慣,在 Qt 中,盡量在構(gòu)造的時(shí)候就指定 parent 對(duì)象,并且大膽在堆上創(chuàng)建。
?
轉(zhuǎn)載于:https://my.oschina.net/daowuming/blog/720738
總結(jié)
以上是生活随笔為你收集整理的【Qt笔记】对象模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python_Django之模板模型
- 下一篇: 大数据2 Hadoop伪分布模式配置部署