Hello Qt——Qt信号槽机制源码解析_qt rangechanged信号,2024年最新最新物联网嵌入式开发开发进阶

70 阅读14分钟

      97,   55,   34,   34, 0x0a,   //第二个自定义槽函数的签名存储在qt_meta_stringdata_Object数组中,     //索引是79,即onScoreChanged(int)  // properties: name, type, flags       35,  117, 0x02495103,        // 第一个自定义属性的签名存储在qt_meta_stringdata_Object中,索引是35,即age       55,  117, 0x02495103,     // 第二个自定义属性的签名存储在qt_meta_stringdata_Object中,索引是55,即score      127,  121, 0x0009510b,     // 第三个自定义属性的签名存储在qt_meta_stringdata_Object中,索引是127,即level  // properties: notify_signal_id    //属性关联的信号编号        0,        1,        0,  // enums: name, flags, count, data      121, 0x0,    4,   54,        //枚举的定义,存储在qt_meta_stringdata_Object中,索引是121,即Level,内含4个枚举常量

  // enum data: key, value          //枚举数据的键值对      133, uint(Object::Basic),    //数组索引是133,即Basic      139, uint(Object::Middle),    //数组索引是139,即Middle      146, uint(Object::Advanced),  //数组索引是146,即Advanced      155, uint(Object::Master),    //数组索引是155,即Master        0        // eod   元数据结束标记 };


内省表是一个 uint 数组,分为五个部分:第一部分content,即内容,分为9行。第一行revision,指MOC生成代码的版本号(Qt4 是6,Qt5则是7)。第二个classname,即类名,该值是一个索引,指向字符串表的某一个位置(本例中就是第0位)。



static const char qt_meta_stringdata_Object[] = {     "Object\0Scorpio\0Author\0""1.0\0Version\0\0"     "age\0ageChanged(int)\0score\0scoreChanged(int)\0"     "onAgeChanged(int)\0onScoreChanged(int)\0"     "int\0Level\0level\0Basic\0Middle\0Advanced\0"     "Master\0" };


#### ******5、信号的实现******


MOC在生成的moc\_xxx.cpp文件中实现了信号,创建了一个指向参数的指针的数组,并将指针数组传给QMetaObject::activate函数。数组的第一个元素是返回值。本例中值是0,因为返回值是void。传给activate函数的第三个参数是信号的索引(本例中是0)。



// SIGNAL 0,ageChanged信号的实现 void Object::ageChanged(int _t1) {     void _a[] = { 0, const_cast<void>(reinterpret_cast<const void*>(&_t1)) };     QMetaObject::activate(this, &staticMetaObject, 0, _a); }

// SIGNAL 1  scoreChanged信号的实现 void Object::scoreChanged(int _t1) {     void _a[] = { 0, const_cast<void>(reinterpret_cast<const void*>(&_t1)) };     QMetaObject::activate(this, &staticMetaObject, 1, _a); }


#### ******6、槽函数的调用******


利用槽函数在qt\_static\_metacall 函数的索引位置来调用槽函数:



void Object::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void**_a) {     if (_c == QMetaObject::InvokeMetaMethod) {         Q_ASSERT(staticMetaObject.cast(_o));         Object *_t = static_cast<Object *>(_o);         switch (_id) {         case 0: _t->ageChanged((reinterpret_cast< int()>(_a[1]))); break;         case 1: _t->scoreChanged((reinterpret_cast< int()>(_a[1]))); break;         case 2: _t->onAgeChanged((reinterpret_cast< int()>(_a[1]))); break;         case 3: _t->onScoreChanged((reinterpret_cast< int()>(_a[1]))); break;         default: ;         }     } }


#### ******7、元对象中的索引******


在每一个QMetaObject对象中,槽、信号以及其它的对象可调用函数都会分配一个从0开始的索引。索引是有顺序的,信号在第一位,槽在第二位,最后是其它函数。这个索引在内部被称为相对索引,不包含父对象的索引位。


为了实现包含在继承链中其它函数的索引,在相对索引的基础上添加一个偏移量,得到绝对索引。绝对索引是在公开API中使用的索引,由QMetaObject::indexOf(Signal, Slot, Method) 类似的函数返回。


连接机制使用以信号为索引的向量。但是在向量中,所有的槽也会占有一定空间,通常在一个对象中,槽的数量要比信号多。所以从 Qt 4.6开始,使用的是一种仅包含信号索引的新的内部实现。


#### ******8、信号与槽的连接******


开始连接时,Qt所要做的第一件事是找出所需要的信号和槽的索引。Qt会去查找元对象的字符串表来找出相应的索引。


然后,创建一个 QObjectPrivate::Connection 对象,将其添加到内部的链表中。


由于允许多个槽连接到同一个信号,需要为每一个信号添加一个已连接的槽的列表。每一个连接都必须包含接收对象和槽的索引。在接收对象销毁的时候,相应的连接也能够被自动销毁。所以每一个接收对象都需要知道谁连接到它自己,以便能够清理连接。


QObject对象的私有数据QObjectPrivate如下:



class Q_CORE_EXPORT QObjectPrivate : public QObjectData {     Q_DECLARE_PUBLIC(QObject) public:     struct ExtraData     {         ExtraData() {}         QList propertyNames;         QList propertyValues;     };

    typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);     struct Connection     {         QObject *sender;         QObject *receiver;         StaticMetaCallFunction callFunction;         // The next pointer for the singly-linked ConnectionList         Connection *nextConnectionList;         //senders linked list         Connection *next;         Connection **prev;         QBasicAtomicPointer argumentTypes;         ushort method_offset;         ushort method_relative;         ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking         ~Connection();         int method() const { return method_offset + method_relative; }     };

    // ConnectionList is a singly-linked list     struct ConnectionList {         ConnectionList() : first(0), last(0) {}         Connection *first;         Connection *last;     };

    struct Sender     {         QObject *sender;         int signal;         int ref;     };

    QObjectPrivate(int version = QObjectPrivateVersion);     virtual ~QObjectPrivate();     void deleteChildren();

    void setParent_helper(QObject *);     void moveToThread_helper();     void setThreadData_helper(QThreadData *currentData, QThreadData *targetData);     void _q_reregisterTimers(void *pointer);

    bool isSender(const QObject *receiver, const char *signal) const;     QObjectList receiverList(const char *signal) const;     QObjectList senderList() const;

    void addConnection(int signal, Connection *c);     void cleanConnectionLists();

    static inline Sender *setCurrentSender(QObject *receiver,                                     Sender *sender);

    static inline void resetCurrentSender(QObject *receiver,                                    Sender *currentSender,                                    Sender *previousSender);

    static void clearGuards(QObject *);

    static QObjectPrivate *get(QObject *o) {         return o->d_func();     }

    int signalIndex(const char *signalName) const;     inline bool isSignalConnected(uint signalIdx) const;

    // To allow arbitrary objects to call connectNotify()/disconnectNotify() without making     // the API public in QObject. This is used by QDeclarativeNotifierEndpoint.     inline void connectNotify(const char *signal);     inline void disconnectNotify(const char *signal);

    static inline void signalSignature(const QMetaMethod &signal,                                        QVarLengthArray *result); public:     QString objectName;     ExtraData *extraData;    // extra data set by the user     QThreadData *threadData; // id of the thread that owns the object     QObjectConnectionListVector *connectionLists;//连接链表向量容器     Connection *senders;     // linked list of connections connected to this object     Sender *currentSender;   // object currently activating the object     mutable quint32 connectedSignals[2];     // preserve binary compatibility with code compiled without Qt 3 support     // keeping the binary layout stable helps the Qt Creator debugger     void *unused;

    QList<QPointer > eventFilters;     union {         QObject *currentChildBeingDeleted;         QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module     };

    // these objects are all used to indicate that a QObject was deleted     // plus QPointer, which keeps a separate list     QAtomicPointerQtSharedPointer::ExternalRefCountData sharedRefcount; };


每一个QObject对象都有一个连接链表容器QObjectConnectionListVector \*connectionLists:将每一个信号与一个 QObjectPrivate::Connection 的链表关联起来。


 QObject::connect函数的实现如下:



bool QObject::connect(const QObject *sender, const char *signal,                       const QObject *receiver, const char *method,                       Qt::ConnectionType type) {     {         const void *cbdata[] = { sender, signal, receiver, method, &type };         if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))             return true;     }

    if (type == Qt::AutoCompatConnection) {         type = Qt::AutoConnection;     }

    if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {         qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",                  sender ? sender->metaObject()->className() : "(null)",                  (signal && *signal) ? signal+1 : "(null)",                  receiver ? receiver->metaObject()->className() : "(null)",                  (method && *method) ? method+1 : "(null)");

        return false;     }

    QByteArray tmp_signal_name;

    if (!check_signal_macro(sender, signal, "connect", "bind"))         return false;     const QMetaObject *smeta = sender->metaObject();     const char *signal_arg = signal;     ++signal; //skip code     //在发送者对象的元对象中将信号的相对索引找到     int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);     if (signal_index < 0)     {         // check for normalized signatures         tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);         signal = tmp_signal_name.constData() + 1;         smeta = sender->metaObject();         signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);     }

    if (signal_index < 0)     {         // re-use tmp_signal_name and signal from above         smeta = sender->metaObject();         signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true);     }

    if (signal_index < 0) {         err_method_notfound(sender, signal_arg, "connect");         err_info_about_objects("connect", sender, receiver);         return false;     }

    signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);     int signalOffset, methodOffset;     computeOffsets(smeta, &signalOffset, &methodOffset);     int signal_absolute_index = signal_index + methodOffset;     signal_index += signalOffset;

    QByteArray tmp_method_name;     int membcode = extract_code(method);

    if (!check_method_code(membcode, receiver, method, "connect"))         return false;

    const char *method_arg = method;     ++method; // skip code

    const QMetaObject *rmeta = receiver->metaObject();     //在接受者对象的元对象中将槽函数的相对索引找到     int method_index_relative = -1;     switch (membcode) {     case QSLOT_CODE:         method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);         break;     case QSIGNAL_CODE:         method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);         break;     }

    if (method_index_relative < 0) {         // check for normalized methods         tmp_method_name = QMetaObject::normalizedSignature(method);         method = tmp_method_name.constData();

        // rmeta may have been modified above         rmeta = receiver->metaObject();         switch (membcode) {         case QSLOT_CODE:             method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);             if (method_index_relative < 0)                 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true);             break;         case QSIGNAL_CODE:             method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);

            if (method_index_relative < 0)                 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true);             break;         }     }

    if (method_index_relative < 0) {         err_method_notfound(receiver, method_arg, "connect");         err_info_about_objects("connect", sender, receiver);         return false;     }

    //检查连接参数是否匹配     if (!QMetaObject::checkConnectArgs(signal, method))     {         qWarning("QObject::connect: Incompatible sender/receiver arguments"                  "\n        %s::%s --> %s::%s",                  sender->metaObject()->className(), signal,                  receiver->metaObject()->className(), method);         return false;     }

    int types = 0;     if ((type == Qt::QueuedConnection)             && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))         return false;     //调用QMetaObjectPrivate::connect将信号与槽进行连接     if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types))         return false;     const_cast<QObject>(sender)->connectNotify(signal - 1);     return true; }


QObject::connect函数的主要功能是在接受者对象的元对象中将槽函数的相对索引找到,在接受者对象的元对象中将槽函数的相对索引找到,最后调用QMetaObjectPrivate::connect将信号与槽进行连接。QObject及其派生类对象的元对象在创建时就有一个QObjectConnectionListVector连接链表容器,QObject::connect的作用就是将新的连接加入到信号发送者附属的元对象的连接链表容器的相应信号的连接链表中(一个信号可能连接多个槽函数)。


![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/eaeedcab92a748faa6e620a9b41d5dff~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771258100&x-signature=jr2k42kYmnzj6TIQn92LEKBvZDs%3D)


 每个QObject及其派生类对象都有一个QObjectConnectionListVector \*connectionLists连接链表容器,将信号的索引作为容器的索引,将每一个信号与一个 QObjectPrivate::ConnectionList链表关联起来。同时,QObjectPrivate::ConnectionList链表中连接的某个槽函数可能是接收者对象的槽函数链表中的一个。每个接收者对象的链表如下:


![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/86df401918cd4ea48f276eb00bab01b0~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771258100&x-signature=EPw7Ri1ULvhA9YqTgtdzVOsQvV0%3D)


 senderList 的 prev 指针是一个指针的指针。这是因为并不是真的指向上一个节点,而是指向上一个节点中的 next 指针。这个指针仅在连接销毁时使用,并且不能向后遍历。它允许不为第一个元素添加特殊处理。




容器中存储的ConnectionList如下:



  struct ConnectionList {         ConnectionList() : first(0), last(0) {}         Connection *first;//第一个结点         Connection *last;//最后一个结点     };


每个ConnectionList类型元素是一个双向链表,保存了信号的所有连接。连接的类型Connection结构如下:



struct Connection {     QObject *sender;//发送者     QObject *receiver;//接受者     StaticMetaCallFunction callFunction;//调用的槽函数     // The next pointer for the singly-linked ConnectionList     Connection *nextConnectionList;     //senders linked list     Connection *next;     Connection **prev;     QBasicAtomicPointer argumentTypes;     ushort method_offset;     ushort method_relative;     ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking     ~Connection();     int method() const { return method_offset + method_relative; } };


QMetaObjectPrivate::connect函数源码如下:  
 //将一个新的连接加入到信号发送者的连接链表容器中相应信号的连接链表中,其中连接加入的连接链表的索引为信号的索引



bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,                                  const QObject *receiver, int method_index,                                  const QMetaObject *rmeta, int type, int *types) {     QObject *s = const_cast<QObject *>(sender);     QObject *r = const_cast<QObject *>(receiver);     int method_offset = rmeta ? rmeta->methodOffset() : 0;     //在元对象的元数据字符串中找到回调的函数指针qt_static_metacall     QObjectPrivate::StaticMetaCallFunction callFunction =         (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata)         ? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0;     QOrderedMutexLocker locker(signalSlotLock(sender),                                signalSlotLock(receiver));

    //如果连接类型为Qt::UniqueConnection     if (type & Qt::UniqueConnection)     {         QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;         if (connectionLists && connectionLists->count() > signal_index)         {             //根据信号索引获取信号的连接             const QObjectPrivate::Connection *c2 =                 (*connectionLists)[signal_index].first;             int method_index_absolute = method_index + method_offset;             while (c2)             {   //如果信号的接收者相同并且槽函数相同,即相同的连接已经存在                 if (c2->receiver == receiver && c2->method() == method_index_absolute)                     return false;//直接返回,                 c2 = c2->nextConnectionList;//下一个信号连接             }         }         type &= Qt::UniqueConnection - 1;     }

    //创建一个新的连接     QObjectPrivate::Connection *c = new QObjectPrivate::Connection;

    //设置连接的属性     c->sender = s;     c->receiver = r;     c->method_relative = method_index;     c->method_offset = method_offset;     c->connectionType = type;     c->argumentTypes = types;     c->nextConnectionList = 0;     c->callFunction = callFunction;//设置回调的函数指针为qt_static_metacall

    QT_TRY     {   //将连接添加到发送者的连接链表容器中相应的信号对应的连接链表中         QObjectPrivate::get(s)->addConnection(signal_index, c);     } QT_CATCH(...) {         delete c;         QT_RETHROW;     }

    c->prev = &(QObjectPrivate::get(r)->senders);     c->next = *c->prev;     *c->prev = c;     if (c->next)         c->next->prev = &c->next;

    QObjectPrivate *const sender_d = QObjectPrivate::get(s);     if (signal_index < 0)     {         sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;     }     else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8)     {         sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));     }

    return true; }


#### ******9、信号的发射******


使用emit发射信号时,实际调用MOC实现的信号函数,信号函数内部调用了QMetaObject::activate()函数。



// SIGNAL 0,ageChanged信号的实现 void Object::ageChanged(int _t1) {     void _a[] = { 0, const_cast<void>(reinterpret_cast<const void*>(&_t1)) };     QMetaObject::activate(this, &staticMetaObject, 0, _a); }

// SIGNAL 1  scoreChanged信号的实现 void Object::scoreChanged(int _t1) {     void _a[] = { 0, const_cast<void>(reinterpret_cast<const void*>(&_t1)) };     QMetaObject::activate(this, &staticMetaObject, 1, _a); }

void QMetaObject::activate(QObject *sender, const QMetaObject *m,                            int local_signal_index,void **argv) {     int signalOffset;     int methodOffset;     computeOffsets(m, &signalOffset, &methodOffset);

    int signal_index = signalOffset + local_signal_index;

    if (!sender->d_func()->isSignalConnected(signal_index))         return; // 如果发送的信号没有槽连接,直接返回

    if (sender->d_func()->blockSig)         return;//如果阻塞,直接返回

    int signal_absolute_index = methodOffset + local_signal_index;

    void *empty_argv[] = { 0 };     if (qt_signal_spy_callback_set.signal_begin_callback != 0)     {         qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index,                                                          argv ? argv : empty_argv);     }

    Qt::HANDLE currentThreadId = QThread::currentThreadId();

    QMutexLocker locker(signalSlotLock(sender));     //获取发送者的连接链表容器     QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;     if (!connectionLists)     {         locker.unlock();         if (qt_signal_spy_callback_set.signal_end_callback != 0)             qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);         return;     }

    ++connectionLists->inUse;

    //从发送者的连接链表容器中使用信号索引作为索引,获取相应的连接链表     const QObjectPrivate::ConnectionList *list;     if (signal_index < connectionLists->count())         list = &connectionLists->at(signal_index);     else         list = &connectionLists->allsignals;     do {         //索取发送的信号的连接链表的第一个连接         QObjectPrivate::Connection *c = list->first;         if (!c) continue;//如果连接为空,继续         // We need to check against last here to ensure that signals added         // during the signal emission are not emitted in this emission.         QObjectPrivate::Connection *last = list->last;         do         {             if (!c->receiver)                 continue;//如果连接的接收者为空,继续

            QObject * const receiver = c->receiver;             const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;

            // determine if this connection should be sent immediately or             // put into the event queue             if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)                     || (c->connectionType == Qt::QueuedConnection))             {                 queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);                 continue; #ifndef QT_NO_THREAD             }             //阻塞队列连接类型             else if (c->connectionType == Qt::BlockingQueuedConnection)             {                 locker.unlock();                 if (receiverInSameThread)                 {                     qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "                              "Sender is %s(%p), receiver is %s(%p)",                              sender->metaObject()->className(), sender,                              receiver->metaObject()->className(), receiver);                 }

                QSemaphore semaphore;                 QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative,                                                                          c->callFunction,

                                                                         sender, signal_absolute_index,

                                                                         0, 0,

                                                                         argv ? argv : empty_argv,

                                                                         &semaphore));                 semaphore.acquire();                 locker.relock();                 continue; #endif             }

            QObjectPrivate::Sender currentSender;             QObjectPrivate::Sender *previousSender = 0;             if (receiverInSameThread)             {                 currentSender.sender = sender;                 currentSender.signal = signal_absolute_index;                 currentSender.ref = 1;                 previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender);             }

            //获取连接的回调函数指针             const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;             const int method_relative = c->method_relative;             //如果连接的方法的偏移小于接收者的元对象的方法的偏移             if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset())             {                 //we compare the vtable to make sure we are not in the destructor of the object.

                locker.unlock();

                if (qt_signal_spy_callback_set.slot_begin_callback != 0)

                    qt_signal_spy_callback_set.slot_begin_callback(receiver, c->method(), argv ? argv : empty_argv);

                //根据接收者的方法偏移,接收者等参数调用qt_static_metacall回调函数

                callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);

                if (qt_signal_spy_callback_set.slot_end_callback != 0)

                    qt_signal_spy_callback_set.slot_end_callback(receiver, c->method());

                locker.relock();

            }             else             {                 const int method = method_relative + c->method_offset;                 locker.unlock();

                if (qt_signal_spy_callback_set.slot_begin_callback != 0)                 {                     qt_signal_spy_callback_set.slot_begin_callback(receiver,                                                                    method,

                                                                   argv ? argv : empty_argv);                 }

                //根据接收者、接收者的方法索引等参数调用发送元对象的metacall

                metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);

                if (qt_signal_spy_callback_set.slot_end_callback != 0)                     qt_signal_spy_callback_set.slot_end_callback(receiver, method);

                locker.relock();             }

            if (receiverInSameThread)                 QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);

            if (connectionLists->orphaned)

                break;

        } while (c != last && (c = c->nextConnectionList) != 0);

        if (connectionLists->orphaned)             break;

    } while (list != &connectionLists->allsignals &&              //start over for all signals;              ((list = &connectionLists->allsignals), true));

    --connectionLists->inUse;     Q_ASSERT(connectionLists->inUse >= 0);     if (connectionLists->orphaned)     {         if (!connectionLists->inUse)             delete connectionLists;     }     else if (connectionLists->dirty)     {         sender->d_func()->cleanConnectionLists();     }

    locker.unlock();

    if (qt_signal_spy_callback_set.signal_end_callback != 0)         qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);

}


metacall函数内部调用了qt\_metacall函数。



int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv) {     if (QMetaObject mo = object->d_ptr->metaObject)         return static_cast<QAbstractDynamicMetaObject>(mo)->metaCall(cl, idx, argv);     else         return object->qt_metacall(cl, idx, argv); }

int Object::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)      {         if (_id < 4)             qt_static_metacall(this, _c, _id, _a);         _id -= 4;     } #ifndef QT_NO_PROPERTIES       else if (_c == QMetaObject::ReadProperty)      {         void *_v = _a[0];         switch (_id) {         case 0: reinterpret_cast< int>(_v) = age(); break;         case 1: reinterpret_cast< int>(_v) = score(); break;         case 2: reinterpret_cast< Level>(_v) = level(); break;         }         _id -= 3;     }      else if (_c == QMetaObject::WriteProperty)      {         void *_v = _a[0];         switch (_id) {         case 0: setAge(reinterpret_cast< int>(_v)); break;         case 1: setScore(reinterpret_cast< int>(_v)); break;         case 2: setLevel(reinterpret_cast< Level>(_v)); break;         }         _id -= 3;     } else if (_c == QMetaObject::ResetProperty) {         _id -= 3;     } else if (_c == QMetaObject::QueryPropertyDesignable) {         _id -= 3;     } else if (_c == QMetaObject::QueryPropertyScriptable) {         _id -= 3;     } else if (_c == QMetaObject::QueryPropertyStored) {         _id -= 3;     } else if (_c == QMetaObject::QueryPropertyEditable) {         _id -= 3;     } else if (_c == QMetaObject::QueryPropertyUser) {         _id -= 3;     }

#endif // QT_NO_PROPERTIES     return _id; }


qt\_metacall函数内部调用了qt\_static\_metacall函数。


#### ******10、槽函数的调用******


槽函数最终通过qt\_static\_metacall函数根据参数调用相应的槽函数。



void Object::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) {     if (_c == QMetaObject::InvokeMetaMethod)      {         Q_ASSERT(staticMetaObject.cast(_o));         Object *_t = static_cast<Object *>(_o);         switch (_id) {         case 0: _t->ageChanged((reinterpret_cast< int()>(_a[1]))); break;         case 1: _t->scoreChanged((reinterpret_cast< int()>(_a[1]))); break;         case 2: _t->onAgeChanged((reinterpret_cast< int()>(_a[1]))); break;         case 3: _t->onScoreChanged((reinterpret_cast< int()>(_a[1]))); break;         default: ;         }     } }


#### ******11、函数调用流程分析******


在onAgeChanged(int age)槽函数内部断点调试。


![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/e60cd9fc069545b7886995b4ee396fe5~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771258100&x-signature=9mPLi%2Fi6MIaJQphF5sRJg2nFZR0%3D)


 得到的函数调用栈如下:



函数调用栈分析:


![](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/b9c0595d28214befb4acb9c0570c914f~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771258100&x-signature=G8GbhFCCOFWBpUXyNWlXXC%2FRzzg%3D)


 Object::qt\_metacall函数内部调用了Object::setAge函数,setAge内部调用Object::ageChanged信号函数,ageChanged信号函数内部调用了QMetaObject::activate函数,activate函数内部调用Object::qt\_static\_metacall函数,最终qt\_static\_metacall函数内部调用了槽函数onAgeChanged。



因此在本例中,当调用ob.setProperty("age", QVariant(30));设置属性时,触发了QMetaProperty::Write函数的调用,进而调用MOC实现的moc\_Object.cpp文件中的Object::qt\_metacall,qt\_metacall内部调用setAge函数,setAge函数内部发射信号ageChanged,即调用Object::ageChanged信号函数,Object::ageChanged函数内部调用了Object对象的元对象的QMetaObject::activate函数,activate函数内部调用了Object::qt\_static\_metacall函数,最终qt\_static\_metacall内部实现对槽函数onAgeChanged的调用。


本例中,信号和槽处于同一线程,连接类型为直接连接,因此属于同步调用,是最简单的调用类型。QMetaObject::activate函数内部实际上根据Object对象的元对象中的信号连接链表容器查找得到信号对应的:qt\_static\_metacall回调函数,进而回调的。


### ******三、信号槽的标准C++实现******


#### ******1、Qt元对象的模拟实现******


Object类的实现:



#ifndef OBJECT_H #define OBJECT_H

#include #include  #include  using namespace std;

//宏定义 #define SLOT(a)     #a #define SIGNAL(a)   #a #define cpp_slots #define cpp_signals protected #define cpp_emit

class Object; //元对象系统,负责搜集信号与槽的名称 struct MetaObject {     //信号组     const char * signal;     //槽组     const char * slot;     //激活某个信号,idx为信号索引     static void active(Object * sender, int idx); };

//被连接对象信息 struct Connection {     Object * receiver;//信号的接收者     int method;//槽函数索引 };

//保存信号索引与连接对象映射 typedef std::multimap<int, Connection> ConnectionMap; typedef std::multimap<int, Connection>::iterator ConnectionMapIt;

//信号和槽的索引查找函数,返回信号或槽的索引 static int find_string(const char * str, const char * substr) {     if (strlen(str) < strlen(substr))         return -1;     int idx = 0;     int len = strlen(substr);     bool start = true;     const char * pos = str;     while (*pos)     {         if (start && !strncmp(pos, substr, len) && pos[len] == '\n')             return idx;

        start = false;         if (*pos == '/n')         {             idx++;             start = true;         }         pos++;     }

    return -1; }

class Object {     static MetaObject meta;//静态元对象声明     void metacall(int idx);//声明元方法调用函数 public:     Object()     {     }     //建立连接     static void cpp_connect(Object* sender, const char* sig, Object* receiver, const char* slt)     {         cout << "connecting a signal to slot..." << endl;         //从元对象数据表中查看信号和槽是否存在         int sig_idx = find_string(sender->meta.signal, sig);         int slt_idx = find_string(receiver->meta.slot, slt);         //如果没有找到信号或者槽         if (sig_idx == -1 || slt_idx == -1)         {             perror("signal or slot not found!");         }         else         {             //创建一个连接,连接内存储接收者和槽函数的索引             Connection c = { receiver, slt_idx };             cout << "add a signal index and an Connection of receiver to sender's Connection map..." << endl;             //将信号的索引和接收者的信息存储到信号发射者的map容器中             sender->connections.insert(std::pair<int, Connection>(sig_idx, c));             cout << "connected success." << endl;         }     }

    void emitSignal()//公有测试函数,发送一个信号     {         cout << "emiting a signal..." << endl;         cpp_emit valueChanged();     }

cpp_signals:     void valueChanged();//信号声明 public cpp_slots:     void onValueChanged()//槽函数     {         cout << "Value Changed."<< endl;     }     friend class MetaObject; private:     ConnectionMap connections;//连接键值对 };

#endif // OBJECT_H


moc\_Object.cpp实现:



#include "Object.h"

//信号的名称 static const char signalNames[] = "valueChanged\n";

//槽的名称 static const char slotNames[] = "onValueChanged\n";

//静态元对象的填充 MetaObject Object::meta = { signalNames, slotNames };

//元方法调用函数的实现,根据连接的索引回调槽函数 void Object::metacall(int idx) {     switch (idx) {     case 0:         onValueChanged();         break;     default:         break;     }; }

//信号的实现 void Object::valueChanged() {     MetaObject::active(this, 0); }

//激活信号 void MetaObject::active(Object* sender, int idx) {     ConnectionMapIt it;     std::pair<ConnectionMapIt, ConnectionMapIt> ret;     ret = sender->connections.equal_range(idx);     for (it = ret.first; it != ret.second; ++it)     {         Connection c = (*it).second;         c.receiver->metacall(c.method);//根据索引调用元方法     } }


#### ******2、信号槽模拟使用******


Main.cpp文件:



#include  #include "Object.h"

using namespace std;

int main(int argc, char *argv[]) {     char p[32] = SLOT(Object);     cout << "cur_value: " << p << endl;     Object obj1, obj2;

    //连接信号和槽     Object::cpp_connect(&obj1, SLOT(valueChanged), &obj2, SIGNAL(onValueChanged));

    //发射一个信号进行测试     obj1.emitSignal();

    getchar();

    return 0; }


### ******四、信号槽的开源实现******


#### ******1、********s********igslot******


sigslot是信号槽的一个非常精炼的C++实现,作者是Sarah Thompson,sigslot实现只有一个头文件sigslot.h,跨平台且线程安全。在WebRTC中,sigslot .h是其基础的事件处理框架, 在多个模块的消息通知,响应处理中被使用。


sigslot库官网:


[sigslot - C++ Signal/Slot Library](https://gitee.com/vip204888)


Sigslot使用示例如下:



#include "sigslot.h" #include  #include <stdio.h> #include  #include <windows.h>

using namespace sigslot;

using namespace std;

class CSender { public:     sigslot::signal2<string, int> m_pfnsigDanger;

    void Panic()     {         static int nVal = 0;         char szVal[20] = { 0 };         sprintf_s(szVal,20, "help--%d", nVal);         m_pfnsigDanger(szVal, nVal++);     } };

class CReceiver :public sigslot::has_slots<> { public:     void OnDanger(string strMsg, int nVal)     {         //printf("%s ==> %d", strMsg.c_str(), nVal);         cout << strMsg.c_str() << " ==> " << nVal << endl;

img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取