转载自:http://blog.csdn.net/superfpe...

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

第一节 Signal和Slot的钥匙

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

它主要有以下三个变量,

const QMetaObject *superdata ;
const char *stringdata ;
const uint *data ;

superdata——父类的QMetaObject 指针,当前类找不到相应的SignalSlot时,程序会到superdata类即父类中查找。换句话说,父类的SignalSlot,子类一样可以使用。
stringdata——一个字符串,保存了类名,SignalSlot函数名,和他们的参数名字
data——一个数组,从这个数组中包含了QMetaObjectPrivate结构的信息,Signal信息,Slot信息还有其他的诸如properties的信息,Signal信息和Slot信息保存了SignalSlot函数名字的起始位置,结合上面的stringdata ,我们可以获得函数名,同时从保存信息的位置,确定了Signal 和Slot 的索引值

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

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 成员变量内容,索引号0 到11 是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 ,说明有四个方法(本例中2 个Signal和,2 个Slot加起来正好是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 ;
} 

abc126
19 声望0 粉丝