Qt工作笔记-Qt元对象系统解析【2合1】
博文轉(zhuǎn)載地址:
https://blog.csdn.net/spwper/article/details/51332187
說Qt信號與槽是一個很好機制,不如說Qt的元對象系統(tǒng)很強大。這也是大家講Qt就必須將信號與槽,講信號與槽就要講Qt的元對象系統(tǒng)。當(dāng)然初學(xué)者知道怎么用就OK啦,當(dāng)然隨著你寫的代碼越多,接觸的平臺越多的時候,你就會好奇Qt是如何把兩個(多個)任意不相關(guān)(必須都繼承與QObject)的對象聯(lián)系在一起的。我們圍繞一些問題來認(rèn)識一下Qt元對象系統(tǒng):
- 什么是Qt元對象系統(tǒng),它包含了哪些內(nèi)容,它在我們的程序中做了什么?
- moc工具是什么,Q_OBJECT宏是什么?
- 元對象系統(tǒng)如何工作在Qml、C++、Javvascrip的混合編程的?
- 信號與槽機制、Qt事件機制、MFC消息機制三者的區(qū)別是什么?
- 信號與槽在單線程與多線程中是如何工作的?
如何在信號與槽機制中,傳遞自定義類型參數(shù)??
什么是Qt元對象系統(tǒng)?
元對象系統(tǒng)是一個基于標(biāo)準(zhǔn)C++的擴展,為Qt提供了1、信號與槽機制2、實時類型信息3、動態(tài)屬性系統(tǒng)。?
這個元對象主要基于三個東西:Object類。大家都知道QObject類是Qt的核心類,很多Qt類都是由它繼承而來,那它具體到底是什么東西呢?我們一起去看下Object的Detail Description我們一起來趴一下它的主要特性:?
(1)它是對象模型的核心,信號與槽是基于對象模型的(兩個對象的連接),而它是對象模型的核心。體現(xiàn)在我們常用的QObject::connect()函數(shù)上,我們后面會分析這個conncet()源碼,趴一下它是怎么工作的。?
(2)對象的組織方式以樹形結(jié)構(gòu)的。這也就是Qt框架那章Core模塊的一個特性“樹形對象模型”。我們常用相關(guān)的函數(shù)體現(xiàn)在QObject::setParent()、QObject::findChild()、QObject::findChilren()這幾個函數(shù)上。這種樹形結(jié)構(gòu)保持了眾多對象之間的嚴(yán)密的父子、邏輯關(guān)系。?
(3)每一個對象都有一個獨立的名字,并且可以查出該對象的繼承關(guān)系。這些對象不同的名字是我們使用findChild()函數(shù)的關(guān)鍵,也是我們在Qml、C++混合編程時的關(guān)鍵。這里QObject有這個屬性,并不是它自己實現(xiàn)的,是QMetaobject幫助實現(xiàn)的,QMetaObject是設(shè)置這些屬性規(guī)則,并建立對象們之間的關(guān)系的關(guān)鍵。(每個人都有自己名字手機QQ,但是你們怎么相互聯(lián)系呢,它就是幫助建立通訊錄)?
(4)對象在銷毀時會發(fā)出一個信號。這里沒什么好多說的。?
(5)添加安裝事件過濾器。讓對象接受或者不接受某些事件以及事件的處理。我們常用到的有mouseEvent()、timeEvent(),在某些沒有繼承Object類中是不能使用這些函數(shù)的,如QGraphicsItem以及它派生出來的其他圖元類。后面我們會講事件與信號槽的區(qū)別。?
前面(1)(2)(3)是Object與元對象系統(tǒng)緊密聯(lián)系的屬性,其他屬性大家可以去看看幫助文檔扒一扒,小白英文太爛。到這里你只要明白為什么元對象非要和QObject相關(guān)就行啦。- Q_OBJECT。簡單理解就是一些宏定義代碼,就是你們自己定義的一些類、類的信號、槽函數(shù)、(Qml混合編程的屬性、自己注冊的Qt數(shù)據(jù)類型等)這么多屬性,怎么保存到通訊錄里面呢?就是通過這個宏定義的函數(shù),幫助你們實現(xiàn)的,后面會結(jié)合moc文件講解這個函數(shù)是做什么的。
moc(Meta-Object-Compiler)元對象編譯器,從概念上和其他編譯器一樣來理解就好了。signals、slots關(guān)鍵字并不是標(biāo)準(zhǔn)C++里面的東西,代碼最后要交給C++編譯器,那么就需要把這部分轉(zhuǎn)化成C++編譯器認(rèn)識的東西,這個工作就是moc來完成了。這里需要注意的是,moc過程是發(fā)生在預(yù)編譯之前的,簡單說就是moc之后每一個包含Q_OBJECT宏頭文件,都會根據(jù)該頭文件里面的signals、slots、Q_MENU l來生成以moc_XXXX(自定義類名)的.cpp文件,我們常用IDE的構(gòu)建生成的.o文件,就是最終的目標(biāo)文件(包含moc生成的cpp)。這個中間生成用qamke生成Makefile可以清楚的看到編譯文件的連接情況。后面講Qt工程的時候會講解Makefile。這里大家想要理解moc更多的使用規(guī)則,幫助里面輸入moc查看幫助文檔,這里推薦也個中文翻譯版(小白英語實在太爛)http://www.kuqin.com/qtdocument/moc.html?
其實大家都知道這三個基本原則,小白在這里也碰到一個疑問,在《零基礎(chǔ)學(xué)Qt4編程》里面說moc在生成cpp文件的同時,也會生成頭文件XXXX.moc.h格式,很顯然小白是沒有這個文件的,在幫助里看到的也是read a C++ source file。C++編譯器在預(yù)編譯處理過程中就是處理頭文件,如果Qt沒有轉(zhuǎn)換頭文件,C++編譯器怎么認(rèn)識signals、slots這樣的關(guān)鍵字呢?在此不知道有沒有熟悉編譯原理里的大牛給小白普及一下。?
元對象系統(tǒng)除了提供信號與槽(communication between objects ,the main reason for introducing the system。主要特性)。我們常用的國際化QObject::tr()、qsTr()還有常用的QObject::setProperty()、QObject::property()。到這里我們大致的理解開始我們提出問題的前三個,每個object都有自己的name,這是我們混合編程的前提,至于它是怎么查找的我們下一節(jié)來扒。
第二篇:
博文地址:https://blog.csdn.net/spwper/article/details/51351793
我們來看一個信號與槽的小例子?
頭文件:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
實現(xiàn)文件:
#include <QApplication> #include <QObject> #include "myclass.h" #include <iostream> using std::cout; using std::endl; myClass::myClass(QObject *parent):QObject(parent) {//Qt4里面信號與槽的連接方式 // connect(this , SIGNAL(signalOne(QString)) , this ,SLOT(slotOne(QString) , Qt::AutoConnection); // connect(this , SIGNAL(signalTwo(int)) , this ,SLOT(slotTwo(int) , Qt::AutoConnection);//Qt5的連接方式,推薦使用。大家可以去看下這兩者的區(qū)別connect(this , &myClass::signalOne , this , &myClass::slotOne , Qt::AutoConnection);connect(this , &myClass::signalTwo , this , &myClass::slotTwo , Qt::AutoConnection);triggerOne();triggerTwo(); } myClass::~myClass() {} void myClass::triggerOne() {emit signalOne("SignalOne"); } void myClass::triggerTwo() {emit signalTwo(88); } QString myClass::slotOne(QString msg) {cout<<"this is slot one::"+msg.toStdString()<<endl;return msg; } int myClass::slotTwo(int a) {cout<<"this is slot two::"<<a<<endl;return a;}int main(int argc, char *argv[]) {QApplication a(argc, argv);myClass test;return a.exec(); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
在寫這個小例子過程中,小白腦殘的本來想寫在一個cpp文件中。最后發(fā)現(xiàn)不行。查資料才知道,moc工具識別Q_OBJECT宏定義是在頭文件中去識別的,些就是說在cpp文件里面包含這個宏是沒有用的。詳細(xì)看此貼http://blog.chinaunix.net/uid-20801802-id-1839159.html?
幫助里面的“read a C++ source file”也只是形象的說法。
下面我們來慢慢分析Q_OBJEC是怎么實現(xiàn)moc_myclass.cpp文件的。?
1、我們看一下Q_OBJECT在Qt庫文件(5.5版本)的定義。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
moc的第一工作肯定是統(tǒng)計分析開發(fā)者寫的這個類有哪些信號槽以及各自的參數(shù)情況:?
這里是moc生成moc_mycalss.cpp文件信號槽數(shù)量數(shù)據(jù)代碼:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
這里前面的數(shù)字注釋都說的很清楚,基本數(shù)據(jù)的統(tǒng)計。在signals和slots里面的:1、3、4、6表示自己在字符串:”myClass\0signalOne\0\0signalTwo\0slotOne\0”?
“msg\0slotTwo\0a”中從第幾個“\0”開始表示自己的信息。這里從偏移量開始signal的第一個”\0”后面表示信號名稱,第二個”\0”表示返回值(同學(xué)要問了,信號函數(shù)實現(xiàn)都沒有哪來的返回值,對于異步調(diào)用返回值永遠(yuǎn)是默認(rèn)構(gòu)造函數(shù)出來的,因為調(diào)用的函數(shù)都沒執(zhí)行完,只能默認(rèn)一個返回值,對于同步調(diào)用,則是最后連接的那個函數(shù)的執(zhí)行的返回值,如果中間有不匹配的情況,Qt會幫你解決,默認(rèn)了),同理后面的slots更好理解了。?
(1)static const QMetaObject staticMetaObject;?
其對應(yīng)的moc文件部分(set):
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
看別人的博客是沒有:?
typedef void (StaticMetacallFunction)(QObject?, QMetaObject::Call, int, void **);?
StaticMetacallFunction static_metacall;?
const QMetaObject * const *relatedMetaObjects;?
這三項的,不知道別人使用的是哪個版本。講道理的話這里,在多類繼承的情況下,是有記錄關(guān)聯(lián)類信息的,但看代碼也只有一個。但是在多類繼承的時候,想要擁有QObject的屬性時候,具有QObject屬性的父類必須放在第一位。大牛說moc規(guī)定多繼承的情況下,moc會假設(shè)第一個繼承的類為QObject, 并且必須要保證在多繼承中,只有唯一一個類是繼承自QObject的。這樣看上去,多余一個QObject繼承的,第二個QObject根本沒辦法識別出來。這里小白只知道要放在第一個,不清楚moc的具體工作流程。?
get部分:
- 1
- 2
- 3
- 4
這里的d_ptr是一個Qt里面的模板類指針,并不是QObject的靜態(tài)成員,這里我在幫助文檔里也沒找到dynamicMetaObject()成員函數(shù),猜測是一個安全處理,返回staticMetaObject。?
(2)virtual void?qt_metacast(const char?);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
當(dāng)傳入的字符串?dāng)?shù)據(jù)是當(dāng)前這個類的話,就將this指針轉(zhuǎn)換成void指針傳給外界: 這個操作相當(dāng)危險。
如果不是當(dāng)前類的話,就調(diào)用父類的qt_metacast繼續(xù)查詢。
在這里,我的父類是QObject,所以自然就調(diào)用QObject::qt_metacase了。?
(3)virtual int qt_metacall(QMetaObject::Call, int, void?);**
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
看名字也知道這里該類所有信號處理函數(shù),并不是我們常用的slots。當(dāng)我們emit一個信號時,也是調(diào)用該函數(shù)再調(diào)用真正的信號函數(shù),當(dāng)其他關(guān)聯(lián)信號過來是,才是調(diào)用真正的槽函數(shù)。
真正的信號函數(shù):
// SIGNAL 0 void myClass::signalOne(QString _t1) {void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };QMetaObject::activate(this, &staticMetaObject, 0, _a); }// SIGNAL 1 void myClass::signalTwo(int _t1) {void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };QMetaObject::activate(this, &staticMetaObject, 1, _a); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
我在幫助里并沒有找到activate函數(shù),看大牛們的說的就是:當(dāng)執(zhí)行流程進入activate中,會先從connectionLists中取出這個signal相對應(yīng)的connection list,而這個總的連接表是由connect()函數(shù)完成的,下章我們會講到。看上面兩個函數(shù)的第三個參數(shù),代表的就是這個類的第幾個信號的索引,類的信號槽索引是一起編排的。
很多地方小白也是半知半解,不知道講清楚沒有。到這里moc就是把Qt的信號與槽的那部分相關(guān)代碼轉(zhuǎn)化為C++代碼。如果有大牛,求教育啊。。
總結(jié)
以上是生活随笔為你收集整理的Qt工作笔记-Qt元对象系统解析【2合1】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Qt|C++-OpenGL绘制三角形带
- 下一篇: staf工作笔记-使用stax并行处理获