Qt的Signal和Slot机制(一)

最近在用Qt开发项目,它的Signal和Slot机制引起了我的兴趣,闲暇无聊,看了下源代码,写下了一些自己的心得。但其中难免有错误之处,望各位看官不吝指出。

 

第一节 Signal和Slot的钥匙

 

我们知道Qt 通过”connect” 函数,将一个SignalSlot 对应了起来。为了形成对应,必有一结构来维护和保存这个对应关系。这个结构就是我们的幕后英雄 QMetaObject 。一般我们只会在由Qt 自动生成的moc 打头的cpp 文件里看到。

它主要有以下三个变量,

const QMetaObject *superdata ;

const char *stringdata ;

const uint *data ;

 

superdata ,父类的QMetaObject 指针,当我们在本类找不到相应的SignalSlot 时,可以去父类中找。

换句话说,父类的SignalSlot ,子类一样可以使用。

 

stringdata ,一个字符串,保存了类名,SignalSlot 函数名,和他们的参数名字

 

data ,一个数组,从这个数组中包含了“QMetaObjectPrivate “结构的信息,Signal 信息,Slot 信息还有其他的诸如properties 的信息,Signal 信息和Slot 信息保存了SignalSlot 函数名字的起始位置,结合上面的stringdata ,我们可以获得函数名,同时从保存信息的位置,确定了SignalSlot 的索引值

 

我们来看以下的一个例子,

class QTestA : public QObject

{

       Q_OBJECT

 

public:

       QTestA (QObject *parent );

       ~QTestA ();

 

 

signals:

       void SignalA1 ();

       void SignalA2 (int i );

 

public slots:

 

       void SlotA1 ();

       void SlotA2 (char *szBuf ,int nSize );

 

private:

      

};

 

这是一个继承QObject 的类,它有两个Singal 和两个Slot

以下是它的QMetaObject 内容,取自由Qt 工程自动生成的moc 打头的cpp 文件。

 

static const uint qt_meta_data_QTestA [] = {

  // content:

       2,       // revision

       0,       // classname

       0,    0, // classinfo

       4,   12, // methods

       0,    0, // properties

       0,    0, // enums/sets

       0,     0, // constructors

///////////////////////////////////// 以上部分 QMetaObjectPrivate 结构信息

  // signals: signature, parameters, type, tag, flags

       8,    7,    7,    7, 0x05,

      21,   19,    7,    7, 0x05,

 

  // slots: signature, parameters, type, tag, flags

      35,    7,    7,    7, 0x0a,

      56,   44,    7,    7, 0x0a,

 

       0        // eod

};

它的data 成员变量内容,索引号011 QMetaObjectPrivate “结构的信息。

QMetaObjectPrivate “结构如下:

struct QMetaObjectPrivate

{

    int revision ;

    int className ;

    int classInfoCount , classInfoData ;

    int methodCount , methodData ;

    int propertyCount , propertyData ;

    int enumeratorCount , enumeratorData ;

    int constructorCount , constructorData ;

};

 

 

我们可以看到 qt_meta_data_QTestA [4] 也就是QMetaObjectPrivate 结构中的methodCount 变量 的值为4 ,说明有四个方法(本例中2Signal 和,2Slot 加起来正好是4 )。又可以看到qt_meta_data_QTestA [5] 也就是QMetaObjectPrivate 结构中的methodData 变量 的值为12 ,说明Method 的信息从qt_meta_data_QTestA 的第12 个数组元素开始(即qt_meta_data_QTestA[12] ),正好是 // signals: signature, parameters, type, tag, flags

它下面的两行就是我们所设定的两个Signal 函数的信息。

而在“ // slots: signature, parameters, type, tag, flags ”的下面是两个Slot 函数的信息。

他们的信息都是5 个一组

static const char qt_meta_stringdata_QTestA [] = {

    "QTestA/0/0SignalA1()/0i/0SignalA2(int)/0"

    "SlotA1()/0szBuf,nSize/0SlotA2(char*,int)/0"

};

这是他的stringdata 变量的内容,里面包含了类名( QTestA ),第一个Signal 函数名( SignalA1() ),

,第二个Singal 函数参数名(i ),第二个Signal 函数名( SignalA2(int) , 第一个Slot 函数名( SlotA1()

,第二个Slot 函数参数名( Buf,nSize ),第二个Slot 函数名( SlotA2(char*,int) )。

 

每一部分都用/0 结束,这样便于字符串操作,只要加上 偏移,就能得到该字符串,而不用管有多长。因为有关字符串的操作,都会碰到/0 后自动终止。

 

结合 data 成员变量内容,来自于data 成员变量内容第一条Signal 函数信息 8,    7,    7,    7, 0x05

其中8 ,表示第一个Signal 函数的名字的在“ qt_meta_stringdata_QTestA ”字符串的偏移位置是8 (注意/0 算一个字符,别数错了:p ),所以,指向它的名字的指针位置就是 qt_meta_stringdata_QTestA+8 。如果我们要拷贝它的名字,就非常简单。

出来的值就是 SignalA1()

比如以下代码,

char szSignal[256] = {0};

strcpy(szSignal, qt_meta_stringdata_QTestA+8);

看这时候,用/0 的分割的好处就出来了,我们只需要知道它从什么位置开始,不需要知道在什么位置结束。因为strrcpy 碰到/0 就不会再拷贝了。

 

第二条Signal 函数的信息是“ 21,   19,    7,    7, 0x05

21 表示,第二个Signal 函数名字的偏移位置是21 ,所以他的值是 SignalA2(int)

 

同理,第一条Slot 函数的信息是 35,    7,    7,    7, 0x0a

所以第一个Slot 函数的偏移量是35, 得出的值是 SlotA1()

第二条Slot 函数的信息是“56,   44,    7,    7, 0x0a ”,得出的值是 SlotA2(char*,int)

也许有看管会问,如何知道一条信息是slot 还是signal 呢,看最后一个参数貌似0x05 是表示的Signal

0x0a 表示的是Slot ,但我也看到过其他值。非常奇怪。

 

同时这几条信息的排列顺序就是,函数的索引值

// signals: signature, parameters, type, tag, flags

       8,    7,    7,    7, 0x05, // 表示SignalA1 函数的信息,它的索引值0

      21,   19,    7,    7, 0x05,// 表示SignalA2 函数的信息,它的索引值1

 

  // slots: signature, parameters, type, tag, flags

      35,    7,    7,    7, 0x0a,// 表示SlotA1 函数的信息,它的索引值2

      56,   44,    7,    7, 0x0a,// 表示SlotA2 函数的信息,它的索引值3

 

索引值将会在这个函数使用

int QTestA ::qt_metacall (QMetaObject ::Call _c , int _id , void **_a )

{

    _id = QObject ::qt_metacall (_c , _id , _a );

    if (_id < 0)

        return _id ;

    if (_c == QMetaObject ::InvokeMetaMethod ) {

        switch (_id ) {

        case 0: SignalA1 (); break ;

        case 1: SignalA2 ((*reinterpret_cast < int (*)>(_a [1]))); break ;

        case 2: SlotA1 (); break ;

        case 3: SlotA2 ((*reinterpret_cast < char *(*)>(_a [1])),(*reinterpret_cast < int (*)>(_a [2]))); break ;

        default : ;

         }

        _id -= 4;

    }

    return _id ;

}

 

猜你喜欢

转载自blog.csdn.net/SuperFPE/article/details/5782707