簡(jiǎn)介
本文主要講解Qt是如何實(shí)現(xiàn)反射,以及一點(diǎn)點(diǎn)反射使用的小心得。
文章概覽
Qt反射內(nèi)幕小窺
詳細(xì)內(nèi)容
反射前期準(zhǔn)備
得到注冊(cè)的類成員變量
得到注冊(cè)的類成員函數(shù)
訪問(wèn)類成員屬性(get,set)
調(diào)用注冊(cè)的函數(shù)
反射應(yīng)用
總結(jié)
Qt反射內(nèi)幕小窺
Qt反射機(jī)制是基于moc(meta object compiler)實(shí)現(xiàn)的,在這里多插一句(可以說(shuō)Qt所有C++沒(méi)有的特性,幾乎都和這個(gè)有關(guān)系)。但是需要注意的是Qt提供的反射式基本的反射,不支持類的反射,這個(gè)與Java,C#還是有差別的。
moc講解
通常C++的編譯過(guò)程為
預(yù)處理->編譯->鏈接->運(yùn)行
Qt編譯的過(guò)程中,有一個(gè)moc的過(guò)程,在Qt工程構(gòu)建過(guò)程中的qmake其實(shí)就是干這個(gè)事的。moc->預(yù)處理->編譯->鏈接->運(yùn)行。
在moc過(guò)程中,需要處理的事情如下:
1、 識(shí)別一些特殊的宏Q_OBJECT、Q_PROPERTY、Q_INVOKABLE。。。; 如果碰到這些關(guān)鍵字,Qt自然就會(huì)去生成對(duì)應(yīng)的moc文件。
2、 slot,signal自然也是如此。
3、 uidesigner,同樣也是在這個(gè)階段處理的;
詳細(xì)內(nèi)容
反射前期準(zhǔn)備
1、 首先得繼承于Q_Object,同時(shí)需要在class中加入Q_OBJECT,但是Q_Object的構(gòu)造函數(shù)默認(rèn)是私有的不讓繼承。
在類中直接使用Q_GADGET也可以實(shí)現(xiàn)反射,。。。據(jù)說(shuō)只能實(shí)現(xiàn)部分功能,目前我只實(shí)現(xiàn)到能遍歷成員屬性,函數(shù),但是不能訪問(wèn)其中的值。
這個(gè)過(guò)程其實(shí)就是定義QMetaObject的過(guò)程,具體見(jiàn)Qt源碼
2、 注冊(cè)類成員變量需要使用Q_PROPERTY
Q_PROPERTY( type member READ get WRITE set) 其中READ,WRITE是關(guān)鍵字
Type表示成員的類型(不支持自定義類型,對(duì)Qt很多基本類型都支持);
Member代表你給該成員另外起的名字,可以和變量名不同;get,set就是自己在C++函數(shù)里面定義的基本的訪問(wèn)函數(shù)名,不需要寫(xiě)參數(shù)。直接上代碼:
3、 注冊(cè)類成員函數(shù)
如果你希望這個(gè)函數(shù)能夠被反射,那么很簡(jiǎn)單,只需要在類的函數(shù)聲明前加入Q_INVOKABLE關(guān)鍵字。
例如Q_INVOKABLE int func( QString flag );
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int Member1 READ Member1 WRITE setMember1 )
Q_PROPERTY(int Member2 READ Member2 WRITE setMember2 )
Q_PROPERTY(QString MEMBER3 READ Member3 WRITE setMember3 )
public:
explicit MyClass(QObject *parent = 0);
signals:
public slots:
public:
Q_INVOKABLE int Member1();
Q_INVOKABLE int Member2();
Q_INVOKABLE QString Member3();
Q_INVOKABLE void setMember1( int mem1 );
Q_INVOKABLE void setMember2( int mem2 );
Q_INVOKABLE void setMember3( const QString& mem3 );
Q_INVOKABLE int func( QString flag );
private:
int m_member1;
int m_member2;
QString m_member3;
};
得到注冊(cè)的類成員變量
MyClass theObj;
const QMetaObject* metaObj = theObj.metaObject();
//1.遍歷類的屬性
int propertyCnt = metaObj->propertyCount();
for ( int i = 0; i < propertyCnt; ++ i )
{
QMetaProperty oneProperty = metaObj->property( i );
cout << " name: " << oneProperty.name();
cout << " type: " << QVariant::typeToName( oneProperty.type()) << "\n";
}
主要思路就是得到其元對(duì)象,得到其元屬性,然后就能得到你需要的信息,具體的訪問(wèn)函數(shù)有name,type,需要注意的是得到的type是枚舉值,還在Qt提供了typeToName的函數(shù),你可以得到想要的(例如不是空洞的2,而是”int”)。
得到注冊(cè)的類成員函數(shù)
//2.遍歷類的函數(shù)成員
int methodCnt = metaObj->methodCount();
for ( int idx = 0; idx < methodCnt; ++ idx )
{
QMetaMethod oneMethod = metaObj->method( idx );
cout << "--------begin-------" << "\n";
cout << " typeName: " << oneMethod.typeName() << "\n";
cout << " signature: " << oneMethod.signature() << "\n";
cout << " methodType: " << oneMethod.methodType() << "\n";
cout << "--------end---------" << "\n";
}
和遍歷類屬性一致,其實(shí)就是根據(jù)元對(duì)象,得到元函數(shù);
其中typeName代表返回類型,signature只的是函數(shù)的原貌,methodType代表函數(shù)的類型,在Qt中分為三類(槽,信號(hào),普通函數(shù))。
訪問(wèn)類成員屬性(get,set)
//3.使用反射
cout << "-------test property-----------" << "\n";
MyClass newObj;
newObj.setProperty("Member1", 66);
cout << newObj.property( "Member1" ).toString().toStdString() << "\n";
cout << newObj.Member1() << "\n";
cout << "--------end----------" << "\n";
在這里使用的是QObject的property() 和setProperty方法,來(lái)訪問(wèn)成員信息。但是對(duì)于使用Q_GADGET宏的類,是不能使用這個(gè)方法的,還在尋找解決方法,基本思路當(dāng)然是重寫(xiě)。
調(diào)用注冊(cè)的函數(shù)
int ret;
MyClass newObj;
newObj.setMember1( 20 );
newObj.setMember2( 50 );
QMetaObject::invokeMethod( &newObj, "func", Qt::DirectConnection,
Q_RETURN_ARG(int, ret ),
Q_ARG(QString, "+"));
//普通函數(shù)的調(diào)用
在MyClass中,我們定義了int func( QString flag );這個(gè)函數(shù),利用反射的調(diào)用方式如上,主要是理解invokeMethod的用法,其中Qt::DirectConnection是函數(shù)的執(zhí)行方式,分為(異步和同步),Q_RETURN_ARG是返回參數(shù),Q_ARG是傳入?yún)?shù),需要按函數(shù)聲明中參數(shù)的順序依次傳入,Qt最多支持9個(gè)參數(shù),對(duì)于一般的應(yīng)用沒(méi)有問(wèn)題。還有疑問(wèn),請(qǐng)移步具見(jiàn)Qt強(qiáng)大的幫助文檔。
反射的應(yīng)用
反射反射,就我目前的認(rèn)知水平來(lái)看,通過(guò)使用字符串,來(lái)實(shí)現(xiàn)函數(shù)的通用化調(diào)用,例如你可以利用反射把很多函數(shù)放置到數(shù)組中,實(shí)現(xiàn)一次遍歷,全部調(diào)用。
目前我見(jiàn)到的大多是利用反射來(lái)操作數(shù)據(jù)庫(kù),例如hibernate,其實(shí)可以利用Qt的反射,快速實(shí)現(xiàn)所謂的hibernate,(最近自己獨(dú)立實(shí)現(xiàn)了一套,很方便)。
總結(jié)
這篇文章主要講了如何使用Qt來(lái)實(shí)現(xiàn)反射,在實(shí)用角度上來(lái)講,我們不需要了解Qt是怎么實(shí)現(xiàn)反射的,對(duì)于怎么用上面說(shuō)的很清楚了。大家有什么的新的想法,希望能提出來(lái),來(lái)電請(qǐng)咨詢
<levard@gmail.com>
posted on 2013-07-13 22:19
Cunch 閱讀(14395)
評(píng)論(1) 編輯 收藏 引用 所屬分類:
C++ 、
Qt