最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

C++模擬實(shí)現(xiàn)Qt的信號(hào)與槽機(jī)制-學(xué)到牛牛

對(duì)于大多學(xué)習(xí)Qt的朋友,心中都有種好奇——那就是Qt最核心的信號(hào)與槽是如何實(shí)現(xiàn)的,對(duì)于小編自己也是一樣,當(dāng)然大家肯定都會(huì)去查閱相關(guān)資料,但大部分時(shí)候也只是一知半解,如果說(shuō)要自己實(shí)現(xiàn)就會(huì)又摸不著頭腦了;所以小編決定自己親自用C++實(shí)現(xiàn)一個(gè)簡(jiǎn)單版的信號(hào)槽,來(lái)理解Qt的實(shí)現(xiàn)原理。于是小編就在翻閱各牛人朋友的博客和反復(fù)研究Qt源碼自己重新寫了一下以便交流學(xué)習(xí)。

我們先還是簡(jiǎn)單的梳理一下Qt信號(hào)與槽的實(shí)現(xiàn)機(jī)理:在Qt中實(shí)現(xiàn)信號(hào)與槽最重要的就是通過(guò)元對(duì)象系統(tǒng)(MOS)的元對(duì)象編譯器(MOC)將我們定義的需要使用到信號(hào)與槽的類中的信號(hào)及信號(hào)調(diào)用槽函數(shù)的方法進(jìn)行定義(這一步就會(huì)生成與源文件對(duì)應(yīng)的moc_xx.cpp文件),然后通過(guò)系統(tǒng)提供的關(guān)聯(lián)方法(connect)將信號(hào)與槽建立一一對(duì)應(yīng)關(guān)系,當(dāng)發(fā)射信號(hào)(其實(shí)就是調(diào)用信號(hào)函數(shù))時(shí)就會(huì)通過(guò)信號(hào)與槽的對(duì)應(yīng)關(guān)系找到對(duì)應(yīng)槽函數(shù)進(jìn)行調(diào)用。這樣的好處就是對(duì)于使用者而言不必去關(guān)心函數(shù)指針回調(diào)函數(shù)這些對(duì)于初學(xué)者比較不太容易搞清晰的東西,簡(jiǎn)化了使用者的操作。當(dāng)然就像我們?cè)谙硎苄腋I畹臅r(shí)候,就一定有人在我們背后默默付出砥礪前行!這里也一樣,對(duì)于我們使用者簡(jiǎn)化了操作,那為了實(shí)現(xiàn)這樣的效果就需要在后臺(tái)提供更多的支持。接下來(lái)我們就通過(guò)代碼再來(lái)梳理一遍。

首先我們使用信號(hào)與槽肯定就會(huì)有信號(hào)的發(fā)送者與接收者,所以我們就先去定義這兩個(gè)類對(duì)象:

sender.h

#pragma once

#include "object.h"

class Sender : public Object

{

? ? X_OBJECT

public:

? ? Sender(int n = 0) : m_num(n){

? ? }? ?

? ? void sendSig();?

signals:

? ? void holdClass(int n);?

? ? int m_num;

};

sender.cpp

#include "sender.h"

void Sender::sendSig()

{

std::cout << "發(fā)送信號(hào):holdClass" << std::endl;

? ? emit holdClass(m_num);

}

在Qt中需要使用信號(hào)槽的對(duì)象都需要直接或間接繼承一個(gè)類QObject,并且需要添加一個(gè)私有宏定義Q_OBJECT,這里就用Object和X_OBJECT代替,signals是Qt中用于聲明信號(hào)函數(shù)的關(guān)鍵字,emit是Qt中用于發(fā)送信號(hào)定義的關(guān)鍵字,這里我們先假設(shè)已經(jīng)有這些類和宏定義,注意信號(hào)函數(shù)是不需要我們定義的,他是在MOC預(yù)處理生成的moc_xx.cpp中自動(dòng)生成定義的,所以這里的cpp很簡(jiǎn)單只有一個(gè)普通函數(shù)sendSig()的定義。同理我們?cè)僮约憾x一個(gè)信號(hào)的接收者對(duì)象和其對(duì)應(yīng)的槽函數(shù)。

receiver.h

#pragma once

#include "object.h"

class Receiver : public Object

{

? ? X_OBJECT

public:

? ? Receiver()? {

? ? }? ?

public slots:

? ? void? attendClass(int n);?

};

receiver.cpp

#include "receiver.h"


void Receiver::attendClass(int n)

{

? ? std::cout << "執(zhí)行槽函數(shù)attendClass:cur class " << n << std::endl;

}

這里的slots就是Qt中用于標(biāo)識(shí)槽函數(shù)聲明的關(guān)鍵字,槽函數(shù)是需要用戶自己定義的。

然后我們就需要再將發(fā)送者信號(hào)與接收者槽關(guān)聯(lián)起來(lái),我們這就提供一個(gè)主函數(shù)來(lái)模擬關(guān)聯(lián)信號(hào)與槽,讓發(fā)送者產(chǎn)生信號(hào):

main.cpp

#include "sender.h"

#include "receiver.h"

int main()

{

? ? Sender xuedao(9527);

? ? Receiver rjc;

? ? Object::connect(&xuedao, signal(holdClass(int)), &rjc, SLOT(attendClass(int)));

? ? xuedao.sendSig();

? ? return 0;

}

這里的signal與SLOT在Qt中就是兩個(gè)轉(zhuǎn)換字符串的宏定義,connect是QObject的一個(gè)靜態(tài)函數(shù)方法。

我們要想這個(gè)程序能正常運(yùn)行起來(lái),接下來(lái)我們就需要去定義一個(gè)類似QObject的Object類和上面需要用到的關(guān)鍵字與宏定義,以及模擬MOC預(yù)處理產(chǎn)生對(duì)應(yīng)的moc_xx.cpp,里面細(xì)節(jié)的地方為了方便理解我都通過(guò)代碼注釋解釋說(shuō)明了

object.h

#pragma once

#include <iostream>

#include <map>

#include <vector>

#include <string>

#define signals protected

#define slots?

#define emit?

#define SLOT(slt)? ? "1"#slt? ? ? // 1用于標(biāo)識(shí)槽函數(shù)

#define signal(sig)? "2"#sig? ? ? //2用于標(biāo)識(shí)信號(hào)

class Object;

struct MetaObject

{

? ? //每個(gè)對(duì)象可能會(huì)有多個(gè)信號(hào)與槽函數(shù),這里就用兩個(gè)vector分別保存信號(hào)與槽函數(shù)信息操作起來(lái)方便點(diǎn)

? ? std::vector<std::string> sigs;

? ? std::vector<std::string> slts;

? ? //activate的功能是通過(guò)信號(hào)發(fā)送者即信號(hào)索引找到關(guān)聯(lián)接收者和方法索引并調(diào)用對(duì)應(yīng)方法

? ? static void activate(Object *sender, int idx, void **argv);? //void **argv對(duì)應(yīng)信號(hào)傳遞的參數(shù)

? ? struct Connection? ?//用于打包信號(hào)接收者與方法的索引(對(duì)應(yīng)上面定義的vector中的信號(hào)槽的索引)

? ? {? ?

? ? ? ? Object *m_receiver;

? ? ? ? int method;

? ? };

};

//Q_OBJECT宏中定義的比較多這里只選擇了我們需使用的幾個(gè)

//static MetaObject meta用于保存使用該宏定義對(duì)象中的信號(hào)與槽信號(hào)與槽的相關(guān)信息?

//getMetaObject()用于返回發(fā)送者或接收者對(duì)象中的static MetaObject meta對(duì)象

#define X_OBJECT static MetaObject meta;? ? ? ? ? ? ? ? \

? ? ? ? ? ? ? ? virtual MetaObject *getMetaObject();? ? \

? ? ? ? ? ? ? ? virtual void metaCall(int idx, void **argv);? //idx為對(duì)應(yīng)槽函數(shù)的索引,void**argv用于接收信號(hào)傳遞的參數(shù)

class Object? ?//需要使用信號(hào)槽對(duì)象的公共接口對(duì)象??

{

? ? X_OBJECT

public:

? ? virtual ~Object() {}

? ? //connect用于建立信號(hào)與槽的關(guān)聯(lián)信息

? ? static void connect(Object *sender, const char *s1, Object *receiver, const char *s2);

private:

? ? friend class MetaObject; //用于方便meta對(duì)象訪問(wèn)下面的信號(hào)槽map

? ? std::multimap<int, MetaObject::Connection> mp; //用于保存信號(hào)索引與接收者對(duì)象即索引的對(duì)應(yīng)關(guān)系

? ? //由于一個(gè)信號(hào)可以對(duì)應(yīng)多個(gè)槽,同樣多個(gè)信號(hào)也可以對(duì)應(yīng)一個(gè)槽,所以這里選用了multimap容器做對(duì)應(yīng)關(guān)系映射

};

object.cpp

#include "object.h"

#include <string.h>? //調(diào)用strcmp函數(shù)需要包含

void MetaObject::activate(Object *sender, int idx, void **argv)

{

? ? //在信號(hào)槽對(duì)應(yīng)關(guān)系的mp中找到發(fā)送者idx索引信號(hào)對(duì)應(yīng)的接收者及關(guān)聯(lián)方法的調(diào)用

? ? auto ptr = sender->mp.equal_range(idx);

? ? for(auto it = ptr.first; it != ptr.second; it++) {

? ? ? ? MetaObject::Connection con = it->second;

? ? ? ? con.m_receiver->metaCall(con.method, argv);? //調(diào)用接收者與發(fā)送者信號(hào)關(guān)聯(lián)的方法,并傳遞需要的參數(shù)

? ? }

}

void Object::connect(Object *sender, const char *s1, Object *receiver, const char *s2)

{

? ? int sig_idx = -1, slt_idx = -1;

? ? MetaObject *senderMeta = sender->getMetaObject();? ? ? ?//獲取發(fā)送者中保存的meta對(duì)象

? ? MetaObject *receiverMeta = receiver->getMetaObject();? ?//獲取接收中保存的meta對(duì)象

? ? //比對(duì)信號(hào)名稱找到對(duì)應(yīng)的信號(hào)索引

? ? for(int i = 0; i < senderMeta->sigs.size(); i++) {

? ? ? ? if(0 == strcmp(s1+1, senderMeta->sigs[i].c_str())) {

? ? ? ? ? ? sig_idx = i;

? ? ? ? }

? ? }

//這里確認(rèn)是槽函數(shù),并找到對(duì)應(yīng)的槽函數(shù)索引

//如果有信號(hào)與信號(hào)關(guān)聯(lián)的情況這里就需要去查找接收者對(duì)應(yīng)的信號(hào)索引,這里省略了

if('1' == *s2) {? ?

? ? ? ? for(int i = 0; i < receiverMeta->slts.size(); i++) {

? ? ? ? ? ? if(0 == strcmp(s2+1, receiverMeta->slts[i].c_str())) {

? ? ? ? ? ? ? ? slt_idx = i;

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? if(-1 == sig_idx || -1 == slt_idx) {

? ? ? ? std::cout << "no match sig or slt" << std::endl;

? ? }

? ? //利用multimap建立信號(hào)索引與接收者和方法索引的對(duì)應(yīng)關(guān)系

? ? MetaObject::Connection con = {receiver, slt_idx};

? ? sender->mp.insert(std::make_pair(sig_idx, con));

}

//下面的主要是預(yù)留的方便父類調(diào)用子類重寫方法的接口這里簡(jiǎn)單定義即可

void Object::metaCall(int idx, void **ag)

{

}

MetaObject Object::meta;

MetaObject *Object::getMetaObject()

{

? ? return &meta;

}

下面就輪到MOC生成的moc_xx.cpp,這些文件在Qt中是自動(dòng)生成的不需要我們實(shí)現(xiàn),我這里只能手動(dòng)模擬簡(jiǎn)單的實(shí)現(xiàn)發(fā)送者的moc_sender.cpp與接收者的moc_receiver.cpp最終我們編譯程序是需要將這兩個(gè)文件一起編譯才能通過(guò)的。

moc_sender.cpp

#include "sender.h"

//根據(jù)定義的信號(hào)槽順序?qū)⑿盘?hào)與槽函數(shù)名稱進(jìn)行保存,Qt中會(huì)將函數(shù)名稱參數(shù)分開保存處理,這里簡(jiǎn)單模擬以下就好

static const char *sigs_name[] = {"holdClass(int)"};? ?

static const char *slts_name[] = {nullptr}; //空表示當(dāng)前沒有定義對(duì)應(yīng)的函數(shù)??

static std::vector<std::string> sigs(sigs_name, sigs_name+1);

static std::vector<std::string> slts;

MetaObject Sender::meta = {sigs, slts};

//Sender的信號(hào)定義

void Sender::holdClass(int n)??

{

? ? void *arg[] = {(void *)&n};

? ? //調(diào)用MetaObject的靜態(tài)方法activate傳遞當(dāng)前的信號(hào)發(fā)送者對(duì)象、信號(hào)索引及參數(shù)

? ? MetaObject::activate(this, 0, arg);? //0表示當(dāng)前信號(hào)函數(shù)在sigs_name[]中的索引

}

MetaObject *Sender::getMetaObject()

{

? ? return &meta;? //返回Sender的meta對(duì)象

}

void Sender::metaCall(int idx, void **arg)? ?

{

? ? // 我們這里Sender 中沒有槽函數(shù)所以這里沒任何操作

}

moc_receiver.cpp

#include "receiver.h"

static const char *sigs_name[] = {nullptr};??

static const char *slts_name[] = {"attendClass(int)"};

static std::vector<std::string> sigs;

static std::vector<std::string> slts(slts_name, slts_name+1);

MetaObject Receiver::meta = {sigs, slts};?

MetaObject *Receiver::getMetaObject()

{

? ? return &meta;? ?//返回Receiver的meta對(duì)象

}

void Receiver::metaCall(int idx, void **arg)

{? ?

? ? //這里根據(jù)slts_name[]中的索引值調(diào)用對(duì)應(yīng)的槽函數(shù)

? ? if(0 == idx) {

? ? ? ? int n = *((int *)arg[0]);

? ? ? ? attendClass(n);

? ? }? ?

}

有了上面這些文件最后我們只需要將所有的.cpp文件一起編譯運(yùn)行就可以實(shí)現(xiàn)Qt中信號(hào)與槽的效果了:

g++ object.cpp sender.cpp receiver.cpp moc_sender.cpp moc_receiver.cpp main.cpp -o xuedao

也可用其他可使用的編譯器編譯進(jìn)行編譯,這里直接用的g++。

另外如果某個(gè)對(duì)象修改或增刪了信號(hào)或槽就需要去手動(dòng)修改對(duì)應(yīng)的moc_xx.cpp文件即可,Qt中實(shí)現(xiàn)考慮的實(shí)際問(wèn)題會(huì)更多,這里只是把整個(gè)信號(hào)槽關(guān)聯(lián)及調(diào)用流程框架進(jìn)行了梳理,具體的大家可以參考Qt源碼做深入學(xué)習(xí)。

文章來(lái)源:學(xué)到牛牛IT培訓(xùn) www.xuedaon.com

C++模擬實(shí)現(xiàn)Qt的信號(hào)與槽機(jī)制-學(xué)到牛牛的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
安阳市| 永州市| 东山县| 子洲县| 揭阳市| 五家渠市| 喀什市| 富平县| 达州市| 汨罗市| 大理市| 郁南县| 台东市| 井冈山市| 化德县| 堆龙德庆县| 汾西县| 三都| 湖北省| 柳州市| 巴东县| 枣庄市| 东乌| 菏泽市| 呼玛县| 华坪县| 江都市| 剑阁县| 临汾市| 唐山市| 浙江省| 雷波县| 松江区| 瓮安县| 石楼县| 寿宁县| 尤溪县| 大埔县| 黎城县| 黑山县| 来安县|