一、QObject的重要知識(shí)
QObject
是Qt對(duì)象模型的核心。這個(gè)模型的核心特性是一個(gè)強(qiáng)大的無縫對(duì)象通信機(jī)制,即信號(hào)和槽。可以使用connect()
將信號(hào)連接到槽函數(shù),并使用disconnect()
破壞已經(jīng)存在的連接。為了避免永不結(jié)束的通知循環(huán),可以使用blockSignals()
暫時(shí)阻塞信號(hào)。受保護(hù)的函數(shù)connectNotify()
和disconnectNotify()
可以用于跟蹤信號(hào)連接。
Qt中,以QObject
為基礎(chǔ)形成了一棵“對(duì)象樹”,當(dāng)使用另一個(gè)對(duì)象作為父對(duì)象創(chuàng)建QObject
時(shí),該對(duì)象將自動(dòng)將自己添加到父對(duì)象的children()
列表中。此后父對(duì)象擁有該對(duì)象的所有權(quán),則會(huì)自動(dòng)刪除析構(gòu)函數(shù)中的子元素。在開發(fā)中可以使用findChild()
或findChildren()
根據(jù)名稱和可選的類型查找子對(duì)象。
每個(gè)對(duì)象都有一個(gè)objectName()
,可以通過相應(yīng)的metaObject()
找到它的類名(函數(shù):QMetaObject::className()
)。在實(shí)際開發(fā)中可以使用inherits()
函數(shù)確定對(duì)象的類是否繼承了QObject
繼承層次結(jié)構(gòu)中的另一個(gè)類。當(dāng)一個(gè)對(duì)象被刪除時(shí),會(huì)發(fā)出destroyed()
信號(hào),通過這一點(diǎn)可以捕獲此信號(hào),避免對(duì)QObject
進(jìn)行懸掛引用。
二、QObject重要成員函數(shù)
本小節(jié)總結(jié)在開發(fā)中,QObject中常使用的成員函數(shù)和重要宏定義。
1、事件獲取和處理API
/*在此對(duì)象上安裝事件篩選器filterObj*/
voidQObject::installEventFilter(QObject*filterObj)
/*這個(gè)虛擬函數(shù)接收對(duì)象的事件,如果事件e被識(shí)別并處理,則返回true*/
boolQObject::event(QEvent*e)
2、對(duì)象的線程關(guān)聯(lián)API
/*返回對(duì)象所在的線程。*/
QThread*QObject::thread()const
/*更改對(duì)象及其子對(duì)象的線程關(guān)聯(lián)性。如果一個(gè)對(duì)象有父對(duì)象,則不能移動(dòng)該對(duì)象到另一個(gè)線程中*/
voidQObject::moveToThread(QThread*targetThread)
3、獲取子對(duì)象API
/*返回該對(duì)象具有給定名稱的所有可轉(zhuǎn)換為類型T的子對(duì)象,如果沒有此類對(duì)象,則返回一個(gè)空列表*/
QListfindChildren(constQString&name=QString(),Qt::FindChildOptionsoptions=Qt::FindChildrenRecursively)const
/*返回子對(duì)象列表*/
constQObjectList&children()const
4、qobject_cast
函數(shù)原型如下:
Tqobject_cast(QObject*object)
Tqobject_cast(constQObject*object)
如果對(duì)象類型為T(或子類),則將給定的對(duì)象轉(zhuǎn)換為類型T;否則返回nullptr。如果對(duì)象是nullptr,那么它也將返回nullptr。
注意:類T必須繼承(直接或間接)
QObject
并使用Q_OBJECT
宏聲明。
qobject_cast()
函數(shù)的行為類似于標(biāo)準(zhǔn)c++ dynamic_cast()
,它的優(yōu)點(diǎn)是不需要RTTI
(Run-Time Type Identification-運(yùn)行時(shí)類型識(shí)別)支持,并且可以跨動(dòng)態(tài)庫(kù)邊界工作。
5、事件處理相關(guān)函數(shù)
//此虛函數(shù)用于接收對(duì)象的事件,如果事件e被識(shí)別和處理,則返回true。
virtualboolevent(QEvent*e)
//如果此對(duì)象已作為被監(jiān)視對(duì)象的事件過濾器安裝,則過濾事件。
virtualbooleventFilter(QObject*watched,QEvent*event)
//從該對(duì)象中移除事件篩選器對(duì)象obj。
voidremoveEventFilter(QObject*obj)
//在對(duì)象上安裝事件篩選器filterObj
voidinstallEventFilter(QObject*filterObj)
6、定時(shí)器相關(guān)函數(shù)
//啟動(dòng)計(jì)時(shí)器并返回計(jì)時(shí)器標(biāo)識(shí)符
intstartTimer(intinterval,Qt::TimerTypetimerType=Qt::CoarseTimer)
//啟動(dòng)計(jì)時(shí)器并返回計(jì)時(shí)器標(biāo)識(shí)符
intstartTimer(std::millisecondstime,Qt::TimerTypetimerType=Qt::CoarseTimer)
//使用定時(shí)器標(biāo)識(shí)符id終止計(jì)時(shí)器
voidkillTimer(intid)
7、重要宏定義
-
Q_DISABLE_COPY(Class)
禁止對(duì)給定類使用復(fù)制構(gòu)造函數(shù)和賦值運(yùn)算符。
-
Q_DISABLE_COPY_MOVE(Class)
該宏用于禁用給定類的復(fù)制構(gòu)造函數(shù)、賦值運(yùn)算符、移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符,將Q_DISABLE_COPY
和Q_DISABLE_MOVE
組合在一起。
- Q_DISABLE_MOVE(Class 5.13)
禁止對(duì)給定類使用移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值操作符。
- Q_EMIT
當(dāng)希望使用第三方信號(hào)/槽函數(shù)機(jī)制來使用Qt信號(hào)和槽函數(shù)時(shí),可以使用此宏替代emit
關(guān)鍵字來發(fā)出信號(hào)。
- Q_ENUM(...)
這個(gè)宏用于向元對(duì)象系統(tǒng)注冊(cè)一個(gè)枚舉類型。該宏必須放在enum
聲明之后,且放在具有Q_OBJECT
或Q_GADGET
宏的類中。對(duì)于命名空間,應(yīng)該使用Q_ENUM_NS()
。例如:
classMyClass:publicQObject
{
Q_OBJECT
public:
MyClass(QObject*parent=nullptr);
~MyClass();
enumPriority{High,Low,VeryHigh,VeryLow};
Q_ENUM(Priority)
voidsetPriority(Prioritypriority);
Prioritypriority()const;
};
- Q_ENUM_NS(...)
這個(gè)宏向元對(duì)象系統(tǒng)注冊(cè)一個(gè)枚舉類型。它必須放在enum
聲明之后,,且具有Q_NAMESPACE
宏的名稱空間中。與Q_ENUM
相同,但在命名空間中。
- Q_FLAG(...)
這個(gè)宏向元對(duì)象系統(tǒng)中注冊(cè)一個(gè)單標(biāo)記類型。它通常用于類定義中,以聲明給定enum
的值可以用作標(biāo)志,并使用按位或運(yùn)算符進(jìn)行組合。對(duì)于命名空間,應(yīng)該使用Q_FLAG_NS()
。例如:
classQLibrary:publicQObject
{
Q_OBJECT
public:
...
enumLoadHint{
ResolveAllSymbolsHint=0x01,
ExportExternalSymbolsHint=0x02,
LoadArchiveMemberHint=0x04
};
Q_DECLARE_FLAGS(LoadHints,LoadHint)
Q_FLAG(LoadHint)
...
}
- Q_INTERFACES(...)
這個(gè)宏告訴Qt這個(gè)類實(shí)現(xiàn)了哪些接口。在宏定義在實(shí)現(xiàn)插件的時(shí)候使用。例如:
classBasicToolsPlugin:publicQObject,
publicBrushInterface,
publicShapeInterface,
publicFilterInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID"org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface"FILE"basictools.json")
Q_INTERFACES(BrushInterfaceShapeInterfaceFilterInterface)
public:
...
};
- Q_NAMESPACE
Q_NAMESPACE
宏用于將QMetaObject
功能添加到名稱空間。Q_NAMESPACE
可以有Q_CLASSINFO
、Q_ENUM_NS
、Q_FLAG_NS,但不能有Q_ENUM
、Q_FLAG
、Q_PROPERTY
、Q_INVOKABLE
、信號(hào)或槽函數(shù)。
- Q_NAMESPACE_EXPORT(EXPORT_MACRO)
該宏的工作原理與Q_NAMESPACE
宏完全相同。但是,在名稱空間中定義的外部staticMetaObject
變量是用提供的EXPORT_MACRO
限定符聲明的。如果需要從動(dòng)態(tài)庫(kù)導(dǎo)出對(duì)象,該宏定義非常有用。
- Q_OBJECT
Q_OBJECT宏必須出現(xiàn)在類定義的私有部分中,該類定義聲明自己的信號(hào)和槽函數(shù),或者使用Qt元對(duì)象系統(tǒng)提供的其他支持。
- Q_PROPERTY(...)
此宏用于在繼承QObject
的類中聲明屬性。屬性的行為類似于類數(shù)據(jù)成員,但它們具有可通過元對(duì)象系統(tǒng)訪問的其他特性。如下代碼:
Q_PROPERTY(typename
(READgetFunction[WRITEsetFunction]|
MEMBERmemberName[(READgetFunction|WRITEsetFunction)])
[RESETresetFunction]
[NOTIFYnotifySignal]
[REVISIONint]
[DESIGNABLEbool]
[SCRIPTABLEbool]
[STOREDbool]
[USERbool]
[CONSTANT]
[FINAL])
- Q_SIGNAL
這是一個(gè)額外的宏,允許我們將單個(gè)函數(shù)標(biāo)記為信號(hào)。當(dāng)使用不支持信號(hào)或Q_SIGNALS
組的第三方源代碼解析器時(shí),使用這個(gè)宏。
- Q_SLOT
這是一個(gè)額外的宏,它允許將單個(gè)函數(shù)標(biāo)記為槽函數(shù)。當(dāng)使用不支持槽函數(shù)或Q_SLOT
組的第三方源代碼解析器時(shí),使用這個(gè)宏。
三、信號(hào)和槽的連接機(jī)制注意事項(xiàng)
1、【自動(dòng)連接(默認(rèn))】如果信號(hào)是在接收對(duì)象具有關(guān)聯(lián)的線程中發(fā)出的,那么行為與直接連接相同。否則,行為與隊(duì)列連接相同。
2、【直接連接】當(dāng)信號(hào)發(fā)出時(shí),槽函數(shù)被立即調(diào)用。槽函數(shù)在發(fā)射器的線程中執(zhí)行,而發(fā)射器的線程不一定是接收器的線程。
3、【隊(duì)列連接】當(dāng)控制返回到接收方線程的事件循環(huán)時(shí)調(diào)用槽,槽函數(shù)在接收方的線程中執(zhí)行。
4、【阻塞排隊(duì)連接】槽被調(diào)用為排隊(duì)連接,除非當(dāng)前線程阻塞直到槽函數(shù)返回。
注意:使用該阻塞排隊(duì)連接類型連接同一線程中的對(duì)象將導(dǎo)致死鎖。
5、【唯一連接】與自動(dòng)連接相同,但只在不復(fù)制現(xiàn)有連接的情況下才建立連接。也就是說,如果相同的信號(hào)已經(jīng)為相同的對(duì)象連接到相同的槽,那么就不創(chuàng)建連接,并且connect()
返回false
。
連接類型可以通過向connect()
傳遞一個(gè)附加參數(shù)來指定。注意,如果事件循環(huán)運(yùn)行在接收方的線程中,當(dāng)發(fā)送方和接收方位于不同線程中時(shí)使用直接連接是不安全的,這與調(diào)用位于另一個(gè)線程中的對(duì)象上的函數(shù)是不安全的原因相同。
QObject::connect()本身是線程安全的。
對(duì)于隊(duì)列連接,傳遞的參數(shù)必須是Qt元對(duì)象系統(tǒng)已知的類型,因?yàn)镼t需要復(fù)制參數(shù),以便存儲(chǔ)參數(shù);如果參數(shù)類型不是Qt元對(duì)象系統(tǒng)已知的,使用隊(duì)列連接,將獲得錯(cuò)誤提示信息,這時(shí)候則需要在創(chuàng)建連接之前,調(diào)用qRegisterMetaType()
向元對(duì)象系統(tǒng)注冊(cè)該數(shù)據(jù)類型。
四、線程關(guān)聯(lián)性
在Qt中,QObject實(shí)例具有線程相關(guān)性,或者可以理解成QObject
存在于某個(gè)線程中。當(dāng)QObject接收到排隊(duì)的信號(hào)或發(fā)布的事件時(shí),槽函數(shù)或事件處理程序?qū)⒃谠搶?duì)象所在的線程中運(yùn)行,這一點(diǎn)很重要。
注意:如果一個(gè)QObject沒有線程關(guān)聯(lián)(也就是說,如果
thread()
返回0),或者如果它存在于一個(gè)沒有運(yùn)行事件循環(huán)的線程中,那么它就不能接收排隊(duì)的信號(hào)或發(fā)布的事件。
默認(rèn)情況下,QObject實(shí)例存在于創(chuàng)建它的線程中,在實(shí)際開發(fā)中可使用thread()
查詢對(duì)象的線程關(guān)聯(lián),并使用moveToThread()
更改對(duì)象的線程關(guān)聯(lián)。
注意:所有QObject必須與它們的父對(duì)象生活在同一個(gè)線程中。除此之外,還需要知道:
(1)如果涉及的兩個(gè)QObject位于不同的線程中,setParent()
將失敗。
(2)當(dāng)一個(gè)QObject被移動(dòng)到另一個(gè)線程時(shí),它的所有子線程也會(huì)被自動(dòng)移動(dòng)。
(3)如果QObject有一個(gè)父對(duì)象,moveToThread()
將失敗。
(4)如果QObject是在QThread::run()
中創(chuàng)建的,它們不能成為QThread對(duì)象的子對(duì)象,因?yàn)镼Thread并不存在于調(diào)用QThread::run()
的線程中。
QObject的父子關(guān)系必須通過傳遞一個(gè)指向子構(gòu)造函數(shù)的指針或調(diào)用
setParent()
來設(shè)置。如果沒有這個(gè)步驟,當(dāng)調(diào)用moveToThread()時(shí),對(duì)象的成員變量將保持在舊線程中。
審核編輯 :李倩
-
API
+關(guān)注
關(guān)注
2文章
1563瀏覽量
63551 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4371瀏覽量
64225 -
Qt
+關(guān)注
關(guān)注
1文章
313瀏覽量
38808
原文標(biāo)題:Qt的萬能承載者QObject,了解多少?
文章出處:【微信號(hào):嵌入式小生,微信公眾號(hào):嵌入式小生】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
C語言宏定義使用技巧
C語言中常用的宏定義
內(nèi)聯(lián)函數(shù)和宏定義的區(qū)別介紹

一些成熟軟件中常用的宏定義
C語言宏定義與預(yù)處理、函數(shù)和函數(shù)庫(kù)

類的申明和成員函數(shù)定義分離是什么
C語言函數(shù)宏封裝技巧分享
C語言中宏函數(shù)的定義和用法

評(píng)論