徐紅偉@百香果科技
QObject類是所有Qt對(duì)象的基類汁蝶。是Qt對(duì)象模型(Object Model)的基礎(chǔ)轻猖,該模型最核心的特點(diǎn)是對(duì)象間的無(wú)縫通信诸典,即信號(hào)和槽(signals and slots)媒至。
Signals和Slots機(jī)制
signals 和 slots是用于「對(duì)象間」通信的。
signal像(但不是)函數(shù)指針季二,更像C#中的事件檩咱,槽就是「函數(shù)」揭措。
signal-slot的功能比函數(shù)指針更多,比如可以多對(duì)一刻蚯,和一對(duì)多的連接绊含。代價(jià)是調(diào)用速度略慢。
用QObject的 static method:connect()和disconnect()進(jìn)行信號(hào)和槽的連接/解連接炊汹。(這解釋了為何使用信號(hào)-槽的Class中必須加入?Q_OBJECT宏)
自動(dòng)內(nèi)存管理
QObjects使用「對(duì)象樹(shù)」(object-tree)進(jìn)行內(nèi)存管理躬充,啥子意思呢?
QObject(QObject *parent= nullptr)
QPushButton(QWidget *parent = nullptr)
QChart::QChart(QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = ...)
其實(shí)QObject在的構(gòu)造函數(shù)里有個(gè)parent參數(shù)兵扬。如果創(chuàng)建時(shí)指定了parent參數(shù)麻裳,對(duì)象就會(huì)自動(dòng)添加到parent的?children()?的列表中口蝠。此時(shí)parent對(duì)象就會(huì)接管子對(duì)象器钟。就是說(shuō)parent對(duì)象銷毀的時(shí)候,會(huì)自動(dòng)delete所有子對(duì)象妙蔗,不用怕「內(nèi)存泄露」傲霸。
其實(shí)Qt的內(nèi)存管理是「半自動(dòng)」的(參考[3]),delete的子對(duì)象是在棧上分配的眉反,那么delete就會(huì)出問(wèn)題昙啄。所以Qt推薦子對(duì)象用new()函數(shù)生成,而不是直接定義實(shí)體寸五。
[virtual] QObject::~QObject()" All child objects are deleted. If any of these objects are on the stack or global, sooner or later your program will crash."這是QObject析構(gòu)函數(shù)的描述梳凛。如果QObject的child objects在棧上或是全局變量,則程序遲早會(huì)崩潰梳杏。
這就像一棵大樹(shù)韧拒,只要把根砍了(銷毀了),會(huì)自動(dòng)銷毀它的枝葉十性。
QObjects通過(guò) ?event() 函數(shù)接收事件叛溢,并可以過(guò)濾事件,參考?installEventFilter() and?eventFilter()劲适。還可以通過(guò)?childEvent()來(lái)捕獲子對(duì)象的事件楷掉。
其他
當(dāng)QObject對(duì)象被銷毀時(shí),會(huì)發(fā)射 ?destroyed() 信號(hào)霞势,可以用之來(lái)監(jiān)測(cè)信號(hào)的生命周期 烹植。
最后,QObject提供一個(gè)定時(shí)器?QTimer愕贡,用于定時(shí)刊橘。
如果對(duì)象要實(shí)現(xiàn)signals-slots或properties,就必須使用?Q_OBJECT宏颂鸿。推薦在所有QObject的派生類中添加?Q_OBJECT宏促绵,無(wú)論是否用到signals-slots特性。
所有的QWidgets對(duì)象繼承于QObject,用isWidgetType()函數(shù)能快速查看對(duì)象是否為widget败晴。
線程親和性(Thread Affinity)
QObject 具有線程親和性浓冒,即它存在于特定的線程里。當(dāng)它接收到 queued signals 或 posted events 時(shí)尖坤,slots 或 event handlers 在 QObject 所在的線程里運(yùn)行稳懒。(QObject 對(duì)象的 slot成員函數(shù)在其被創(chuàng)建的線程里運(yùn)行,而不是發(fā)射信號(hào)的線程了)慢味。
注意:如果QObject 沒(méi)有親和的線程(即 thread() 函數(shù)返回 0)场梆,則它所在的線程沒(méi)有運(yùn)行事件循環(huán),因此它就無(wú)法接收到 queued signals或posted events.
默認(rèn)情況下纯路,QObject生存在創(chuàng)建它的線程下或油。可以用 thread() 函數(shù)查看它所在線程驰唬,也可以用 moveToThread()改變所在線程顶岸。
所有的 QObjects 都必須和其parent在同一個(gè)線程里,因此:
1.? 如果兩個(gè)QObject在不同的線程里叫编,則不能用setParent() 建立父子關(guān)系辖佣。
2. 當(dāng) QObject 移動(dòng)到另一個(gè)線程里后,其所有子對(duì)象(指定parent關(guān)系的)也自動(dòng)移動(dòng)到相同線程搓逾。
3.如果QObject有父對(duì)象卷谈,則對(duì)其進(jìn)行moveToThread()時(shí)會(huì)失敗。
4.如果QObject在 QThread::run()中創(chuàng)建霞篡,則它不能成為QThread的子對(duì)象世蔗,因?yàn)镼Thread不在QThread::run()線程里。
注意:QObject的成員變量不會(huì)自動(dòng)成為其子對(duì)象寇损。只有現(xiàn)實(shí)調(diào)用setParent()或再子對(duì)象的構(gòu)造函數(shù)里指定父對(duì)象parent凸郑,才能建立父子關(guān)系。因此把QObject移動(dòng)到一個(gè)線程時(shí)矛市,他的成員變量(非子對(duì)象)不會(huì)移動(dòng)芙沥。
無(wú)拷貝構(gòu)造函數(shù)(copy constructor)和=操作
在設(shè)計(jì)QObject時(shí),特意取消了「拷貝構(gòu)造函數(shù)」和 = 操作浊吏。
所在在傳遞QObjet及其子對(duì)象時(shí)而昨,要傳遞指針。在容器類中找田,也只能存儲(chǔ)其指針歌憨。
國(guó)際化
所有QObject的子類都支持Qt的翻譯特性,讓app的UI可以翻譯成不同語(yǔ)言墩衙。為了使UI上的字符可翻譯务嫡,必須用?tr() 函數(shù)包起來(lái)甲抖,詳細(xì)內(nèi)容見(jiàn)Writing Source Code for Translation。
[1]參考Qt官方文檔:“QObejct”