QT 反射機制

#ifndef MYOBJ_H
#define MYOBJ_H

#include <QObject>
#include <qstring.h>
class MyObj : public QObject
{
    Q_OBJECT

    Q_PROPERTY(int ww READ id WRITE setid)

    Q_PROPERTY(QString m_strName READ name WRITE setName)
public:
    explicit MyObj(QObject *parent = 0);

    Q_INVOKABLE int id();

    Q_INVOKABLE void setid(int);

    Q_INVOKABLE QString name();

    Q_INVOKABLE void setName(QString);
signals:

public slots:

private:
    int m_nId;

    QString m_strName;
};

#endif // MYOBJ_H
</pre><pre code_snippet_id="1971431" snippet_file_name="blog_20161107_7_6978204" name="code" class="cpp">
<pre style="margin-top: 0px; margin-bottom: 0px;"><span style=" color:#000080;">#include</span><span style=" color:#c0c0c0;"> </span><span style=" color:#008000;">"myobj.h"</span>

MyObj::MyObj(QObject *parent) : QObject(parent)
{
    m_nId = 0;
}

int MyObj::id()
{
    return m_nId;
}

void MyObj::setid(int nid)
{
    m_nId = nid;
}

QString MyObj::name()
{
    return m_strName;
}

void MyObj::setName(QString str)
{
    m_strName = str;
}


<pre name="code" class="plain">簡介

本文主要講解Qt是如何實現反射,以及一點點反射使用的小心得。

文章概覽

Qt反射內幕小窺

詳細內容

反射前期準備

得到註冊的類成員變量

得到註冊的類成員函數

訪問類成員屬性(get,set)

調用註冊的函數

反射應用

總結

Qt反射內幕小窺

Qt反射機制是基於moc(meta object compiler)實現的,在這裏多插一句(可以說Qt所有C++沒有的特性,幾乎都和這個有關係)。但是需要注意的是Qt提供的反射式基本的反射,不支持類的反射,這個與Java,C#還是有差別的。

moc講解

通常C++的編譯過程爲

預處理->編譯->鏈接->運行

Qt編譯的過程中,有一個moc的過程,在Qt工程構建過程中的qmake其實就是幹這個事的。moc->預處理->編譯->鏈接->運行。

在moc過程中,需要處理的事情如下:

1、 識別一些特殊的宏Q_OBJECT、Q_PROPERTY、Q_INVOKABLE。。。; 如果碰到這些關鍵字,Qt自然就會去生成對應的moc文件。

2、 slot,signal自然也是如此。

3、 uidesigner,同樣也是在這個階段處理的;

詳細內容

 

反射前期準備

1、 首先得繼承於Q_Object,同時需要在class中加入Q_OBJECT,但是Q_Object的構造函數默認是私有的不讓繼承。

在類中直接使用Q_GADGET也可以實現反射,。。。據說只能實現部分功能,目前我只實現到能遍歷成員屬性,函數,但是不能訪問其中的值。

這個過程其實就是定義QMetaObject的過程,具體見Qt源碼

2、 註冊類成員變量需要使用Q_PROPERTY

Q_PROPERTY( type member READ get WRITE set) 其中READ,WRITE是關鍵字

Type表示成員的類型(不支持自定義類型,對Qt很多基本類型都支持);

Member代表你給該成員另外起的名字,可以和變量名不同;get,set就是自己在C++函數裏面定義的基本的訪問函數名,不需要寫參數。直接上代碼:

3、 註冊類成員函數

如果你希望這個函數能夠被反射,那麼很簡單,只需要在類的函數聲明前加入Q_INVOKABLE關鍵字。

例如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;
   };
得到註冊的類成員變量

  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";
  }
主要思路就是得到其元對象,得到其元屬性,然後就能得到你需要的信息,具體的訪問函數有name,type,需要注意的是得到的type是枚舉值,還在Qt提供了typeToName的函數,你可以得到想要的(例如不是空洞的2,而是”int”)。

得到註冊的類成員函數

//2.遍歷類的函數成員
 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";
  }
和遍歷類屬性一致,其實就是根據元對象,得到元函數;

其中typeName代表返回類型,signature只的是函數的原貌,methodType代表函數的類型,在Qt中分爲三類(槽,信號,普通函數)。

訪問類成員屬性(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方法,來訪問成員信息。但是對於使用Q_GADGET宏的類,是不能使用這個方法的,還在尋找解決方法,基本思路當然是重寫。

調用註冊的函數

  int ret;
  MyClass newObj;
 newObj.setMember1( 20 );
 newObj.setMember2( 50 );
 QMetaObject::invokeMethod( &newObj, "func", Qt::DirectConnection,
 Q_RETURN_ARG(int, ret ),
 Q_ARG(QString, "+"));
//普通函數的調用

在MyClass中,我們定義了int func( QString flag );這個函數,利用反射的調用方式如上,主要是理解invokeMethod的用法,其中Qt::DirectConnection是函數的執行方式,分爲(異步和同步),Q_RETURN_ARG是返回參數,Q_ARG是傳入參數,需要按函數聲明中參數的順序依次傳入,Qt最多支持9個參數,對於一般的應用沒有問題。還有疑問,請移步具見Qt強大的幫助文檔。

反射的應用

反射反射,就我目前的認知水平來看,通過使用字符串,來實現函數的通用化調用,例如你可以利用反射把很多函數放置到數組中,實現一次遍歷,全部調用。

目前我見到的大多是利用反射來操作數據庫,例如hibernate,其實可以利用Qt的反射,快速實現所謂的hibernate,(最近自己獨立實現了一套,很方便)。




</pre><pre code_snippet_id="1971431" snippet_file_name="blog_20161107_7_6978204" name="code" class="cpp">


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章