日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

Qt工作笔记-Qt元对象系统解析【2合1】

發(fā)布時間:2025/3/15 windows 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Qt工作笔记-Qt元对象系统解析【2合1】 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

博文轉(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


我們來看一個信號與槽的小例子?
頭文件:

#ifndef MYCLASS #define MYCLASS #include <QObject> class myClass : public QObject {Q_OBJECT public:explicit myClass(QObject *parent = 0 );~myClass();void triggerOne();void triggerTwo(); signals:void signalOne(QString); //自定義信號onevoid signalTwo(int); //自定義信號two private slots: //私有槽函數(shù)只能與自己關(guān)聯(lián) protected slots: //在共有保護派生子類可連接 public slots: //共有繼承派生類可連接QString slotOne(QString msg);int slotTwo(int a);};#endif // MYCLASS
  • 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版本)的定義。

#define Q_OBJECT \ public: \Q_OBJECT_CHECK \QT_WARNING_PUSH \Q_OBJECT_NO_OVERRIDE_WARNING \static const QMetaObject staticMetaObject; \virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \QT_WARNING_POP \QT_TR_FUNCTIONS \ private: \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \struct QPrivateSignal {};
  • 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ù)代碼:

struct qt_meta_stringdata_myClass_t {QByteArrayData data[8];char stringdata0[51]; }; #define QT_MOC_LITERAL(idx, ofs, len) \Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \qptrdiff(offsetof(qt_meta_stringdata_myClass_t, stringdata0) + ofs \- idx * sizeof(QByteArrayData)) \) static const qt_meta_stringdata_myClass_t qt_meta_stringdata_myClass = {{ QT_MOC_LITERAL(0, 0, 7), // "myClass" QT_MOC_LITERAL(1, 8, 9), // "signalOne" QT_MOC_LITERAL(2, 18, 0), // "" QT_MOC_LITERAL(3, 19, 9), // "signalTwo" QT_MOC_LITERAL(4, 29, 7), // "slotOne" QT_MOC_LITERAL(5, 37, 3), // "msg" QT_MOC_LITERAL(6, 41, 7), // "slotTwo" QT_MOC_LITERAL(7, 49, 1) // "a"},"myClass\0signalOne\0\0signalTwo\0slotOne\0""msg\0slotTwo\0a" }; #undef QT_MOC_LITERALstatic const uint qt_meta_data_myClass[] = {// content:7, // revision0, // classname0, 0, // classinfocount,classinfodata4, 14, // methodscount,methoddata0, 0, // propertiescount,propertiesdata0, 0, // enums/sets0, 0, // constructors0, // flags2, // signalCount// signals: name, argc, parameters, tag, flags1, 1, 34, 2, 0x06 /* Public */,3, 1, 37, 2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags4, 1, 40, 2, 0x0a /* Public */,6, 1, 43, 2, 0x0a /* Public */,// signals: parametersQMetaType::Void, QMetaType::QString, 2,QMetaType::Void, QMetaType::Int, 2,// slots: parametersQMetaType::QString, QMetaType::QString, 5,QMetaType::Int, QMetaType::Int, 7,0 // eod };
  • 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):

const QMetaObject myClass::staticMetaObject = {{ &QObject::staticMetaObject, qt_meta_stringdata_myClass.data,qt_meta_data_myClass, qt_static_metacall, Q_NULLPTR, Q_NULLPTR} }; //staticMetaObject靜態(tài)值是由Q_OBJECT引出的,這里只對其自定義部分的參數(shù)進行賦值保存,QMetaObject結(jié)構(gòu)體遠(yuǎn)比這個復(fù)雜,只是其他部分與開發(fā)者自定義內(nèi)容無關(guān),moc編譯器會自行處理,所以這里生成的moc_myclass.cpp不做處理: struct { // private dataconst QMetaObject *superdata; //父類信息const QByteArrayData *stringdata;const uint *data;typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);StaticMetacallFunction static_metacall;const QMetaObject * const *relatedMetaObjects;//其他關(guān)聯(lián)類信息void *extradata; //reserved for future use};
  • 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部分:

const QMetaObject *myClass::metaObject() const {return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; }
  • 1
  • 2
  • 3
  • 4

這里的d_ptr是一個Qt里面的模板類指針,并不是QObject的靜態(tài)成員,這里我在幫助文檔里也沒找到dynamicMetaObject()成員函數(shù),猜測是一個安全處理,返回staticMetaObject。?
(2)virtual void?qt_metacast(const char?);

void *myClass::qt_metacast(const char *_clname) {if (!_clname) return Q_NULLPTR;if (!strcmp(_clname, qt_meta_stringdata_myClass.stringdata0))return static_cast<void*>(const_cast< myClass*>(this));return QObject::qt_metacast(_clname); }
  • 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?);**

void myClass::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) {if (_c == QMetaObject::InvokeMetaMethod) {myClass *_t = static_cast<myClass *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->signalOne((*reinterpret_cast< QString(*)>(_a[1]))); break;case 1: _t->signalTwo((*reinterpret_cast< int(*)>(_a[1]))); break;case 2: { QString _r = _t->slotOne((*reinterpret_cast< QString(*)>(_a[1])));if (_a[0]) *reinterpret_cast< QString*>(_a[0]) = _r; } break;case 3: { int _r = _t->slotTwo((*reinterpret_cast< int(*)>(_a[1])));if (_a[0]) *reinterpret_cast< int*>(_a[0]) = _r; } break;default: ;}} else if (_c == QMetaObject::IndexOfMethod) {int *result = reinterpret_cast<int *>(_a[0]);void **func = reinterpret_cast<void **>(_a[1]);{typedef void (myClass::*_t)(QString );if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&myClass::signalOne)) {*result = 0;}}{typedef void (myClass::*_t)(int );if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&myClass::signalTwo)) {*result = 1;}}} }
  • 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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。