1. 程式人生 > >Qt訊號槽機制原始碼學習

Qt訊號槽機制原始碼學習

簡述

這裡並不全面介紹Qt的訊號槽機制的實現,僅以qt-opensource-windows-x86-msvc2015_64-5.6.0的原始碼為原料,以一個簡單的訊號槽例子為點,打通一條線。下面的原始碼大部分是經過刪減和修改的,為了使原始碼更簡單並且增加可讀性。

簡單的訊號槽例子

在vs2015中新建qt控制檯專案,然後新增一個頭檔案和一個原始檔。
mian.h內容

#pragma once

#include <QObject>

class Counter :public QObject
{
    Q_OBJECT

public:
    Counter() :m_value(0) {}

    void count()
    {
        ++m_value;
        emit valueChanged(m_value);
    }

    public slots:
    void printValue();

signals:
    void valueChanged(int value);
    void valueReset();

private:
    int m_value;
};

main.cpp內容

#include <QCoreApplication>
#include <iostream>
#include "main.h"

void Counter::printValue()
{
    std::cout << m_value << std::endl;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Counter c;
    QObject::connect(&c, &Counter::valueChanged, &c, &Counter::printValue);
    c.count();

    return a.exec();
}
編譯之後qt自動生成moc_main.cpp

#include "../../main.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'main.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.6.0. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif

QT_BEGIN_MOC_NAMESPACE
struct qt_meta_stringdata_Counter_t {
    QByteArrayData data[6];
    char stringdata0[50];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_Counter_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_Counter_t qt_meta_stringdata_Counter = {
    {
QT_MOC_LITERAL(0, 0, 7), // "Counter"
QT_MOC_LITERAL(1, 8, 12), // "valueChanged"
QT_MOC_LITERAL(2, 21, 0), // ""
QT_MOC_LITERAL(3, 22, 5), // "value"
QT_MOC_LITERAL(4, 28, 10), // "valueReset"
QT_MOC_LITERAL(5, 39, 10) // "printValue"

    },
    "Counter\0valueChanged\0\0value\0valueReset\0"
    "printValue"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_Counter[] = {

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       3,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       2,       // signalCount

 // signals: name, argc, parameters, tag, flags
       1,    1,   29,    2, 0x06 /* Public */,
       4,    0,   32,    2, 0x06 /* Public */,

 // slots: name, argc, parameters, tag, flags
       5,    0,   33,    2, 0x0a /* Public */,

 // signals: parameters
    QMetaType::Void, QMetaType::Int,    3,
    QMetaType::Void,

 // slots: parameters
    QMetaType::Void,

       0        // eod
};

void Counter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        Counter *_t = static_cast<Counter *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->valueChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 1: _t->valueReset(); break;
        case 2: _t->printValue(); break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        void **func = reinterpret_cast<void **>(_a[1]);
        {
            typedef void (Counter::*_t)(int );
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Counter::valueChanged)) {
                *result = 0;
                return;
            }
        }
        {
            typedef void (Counter::*_t)();
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Counter::valueReset)) {
                *result = 1;
                return;
            }
        }
    }
}

const QMetaObject Counter::staticMetaObject = {
    { &QObject::staticMetaObject, qt_meta_stringdata_Counter.data,
      qt_meta_data_Counter,  qt_static_metacall, Q_NULLPTR, Q_NULLPTR}
};


const QMetaObject *Counter::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

void *Counter::qt_metacast(const char *_clname)
{
    if (!_clname) return Q_NULLPTR;
    if (!strcmp(_clname, qt_meta_stringdata_Counter.stringdata0))
        return static_cast<void*>(const_cast< Counter*>(this));
    return QObject::qt_metacast(_clname);
}

int Counter::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 < 3)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 3;
    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
        if (_id < 3)
            *reinterpret_cast<int*>(_a[0]) = -1;
        _id -= 3;
    }
    return _id;
}

// SIGNAL 0
void Counter::valueChanged(int _t1)
{
    void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

// SIGNAL 1
void Counter::valueReset()
{
    QMetaObject::activate(this, &staticMetaObject, 1, Q_NULLPTR);
}
QT_END_MOC_NAMESPACE
上面moc_main.cpp的程式碼有些地方不好理解,並且在本例中不需要用到,把這些內容刪除,簡化後如下
static const uint qt_meta_data_Counter[] = {
 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       3,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       2,       // signalCount
};

void Counter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod)
    {   // 根據_id呼叫訊號或者槽函式
        Counter *_t = static_cast<Counter *>(_o);
        switch (_id) {
        case 0: _t->valueChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 1: _t->valueReset(); break;
        case 2: _t->printValue(); break;
        default:;
        }
    }
    else if (_c == QMetaObject::IndexOfMethod)
    {   // 根據訊號函式指標獲取其_id
        int *result = reinterpret_cast<int *>(_a[0]);
        void **func = reinterpret_cast<void **>(_a[1]);
        {
            typedef void (Counter::*_t)(int);
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Counter::valueChanged))
            {
                *result = 0;
                return;
            }
        }
        {
            typedef void (Counter::*_t)();
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Counter::valueReset)) {
                *result = 1;
                return;
            }
        }
    }
}

// 每個QObject或者QObject子型別都包含一個靜態QMetaObject物件
// QMetaObject物件初始化
const QMetaObject Counter::staticMetaObject = {
{
    // 父類元物件指標
        &QObject::staticMetaObject, nullptr,
        qt_meta_data_Counter,
        qt_static_metacall/*d.static_metacall*/, nullptr, nullptr
    }
};

const QMetaObject *Counter::metaObject() const
{
    return &staticMetaObject;
}

// SIGNAL 0
void Counter::valueChanged(int _t1)
{
    // _a[0]是返回值存放地址,這裡返回型別為void,所以是nullptr
    void *_a[] = { nullptr, &_t1 };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

// SIGNAL 1
void Counter::valueReset()
{
    // 既沒有引數也沒有返回值,所以最後一個引數是nullptr
    QMetaObject::activate(this, &staticMetaObject, 1, nullptr);
}

QMetaObject介紹

在這不全面介紹QMetaObject,可以參考Qt手冊,僅僅介紹本例相關的一些QMetaObject實現細節。

Qt手冊上說QMetaObject類包含了Qt物件的元資料,例如類名、父類資訊、方法屬性等等,每個QObject類或子類都對應一個QMetaObject物件。在訊號槽機制中,訊號和槽的連線關係的儲存,訊號觸發時執行槽函式這些操作也需要通過QMetaObject才能進行。

下面是Qt手冊對元物件的介紹:

The meta-object system is based on three things:

1. The QObject class provides a base class for objects thatcan take advantage of the meta-object system.

2. The Q_OBJECT macro inside the private section of theclass declaration is used to enable meta-object features, such as dynamicproperties, signals, and slots.

3. The Meta-Object Compiler (moc) supplies each QObjectsubclass with the necessary code to implement meta-object features.

     The moc toolreads a C++ source file. If it finds one or more class declarations thatcontain the Q_OBJECT macro, it produces another C++ source file which containsthe meta-object code for each of those classes. This generated source file iseither #include'd into the class's source file or, more usually, compiled andlinked with the class's implementation.

上面講到元物件系統基於3個條件,現在結合前面的例子分析這3個條件。

第一個條件是使用元物件系統的類須要是QObject的子類,前面的例子中Counter繼承自QObject,滿足第一個條件。

第二個條件是在類內須要在私有區定義Q_OBJECT巨集,有了這個巨集才有元物件。這個巨集在Counter中也已定義,但是這個巨集為什麼使得元物件可用?首先看一下Q_OBJECT巨集的定義,下面的巨集定義也是經過刪減的。

#define Q_OBJECT \
public: \
    static const QMetaObject staticMetaObject; \
    virtual const QMetaObject *metaObject() const; \
private: \
    static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);

可以看到這個巨集為類定義了一些屬性和函式,回顧上面moc_main.cpp經過簡化的程式碼,發現moc_main.cpp中初始化了staticMetaObject屬性,實現了metaObject函式、qt_static_metacall函式和訊號函式。有了這些就可以通過類獲取類的元物件,如Counter::staticMetaObject,也可以通過物件獲取其真正的型別(metaObject()是虛擬函式,具有執行時多型),如c->metaObject()。

第三個條件是需要元物件編譯器moc的支援,這個Qt是有的,不需要操心。在上面的例子中,並沒有自己實現兩個訊號函式,也沒有實現Q_OBJECT巨集裡面定義的屬性和函式,這些都在moc_main.cpp中由元物件編譯器moc自動實現了。

下面是經過刪減的QMetaObject定義:
struct QMetaObject
{
    // internal index-based signal activation
    static void activate(QObject *, const QMetaObject *, int, void **);
    static void activate(QObject *, int, int, void **);

    enum Call {
        InvokeMetaMethod,
        IndexOfMethod,
    };

    int static_metacall(Call, int, void **) const;
    
    struct { // private data
        typedef void(*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);

        const QMetaObject *superdata;
        const QByteArrayData *stringdata;
        const uint *data;
        StaticMetacallFunction static_metacall;
        const QMetaObject * const *relatedMetaObjects;
        void *extradata; //reserved for future use
    } d;
};

int QMetaObject::static_metacall(Call cl, int idx, void **argv) const
{
    // static_metacall不存在
    if (!d.static_metacall)
        return 0;
    d.static_metacall(0, cl, idx, argv);
    return -1;
}

從上面的可以看到其中的static_metacall只是呼叫了d.static_metacall,那麼d.static_metacall從哪裡來?結合moc_main.cpp中的內容可以瞭解到如果呼叫了Counter裡元物件的QMetaObject::static_metacall方法,實際上會執行moc_main.cpp中的Counter::qt_static_metacall方法。

activate方法:觀察moc_main.cpp檔案中訊號函式的實現,可以瞭解到訊號函式通過這個方法實現功能。activate啟用訊號,執行關聯的訊號或者槽函式,具體的實現後面敘述。

經過上面的分析,可以發現通過QMetaObject可以做很多事情,例如呼叫訊號或者槽函式、獲取類訊號的個數、獲取訊號的index等等。

QObject::connect過程

如果需要在觸發訊號後執行槽函式,首先要將訊號和槽進行連線,在上面的例子中,訊號和槽的連線在mian.cpp中,如下

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Counter c;
    QObject::connect(&c, &Counter::valueChanged, &c, &Counter::printValue);
    c.count();

    return a.exec();
}

main函式中首先定義了c,然後關聯訊號valueChanged和槽printValue,最後呼叫c.count()並在count()函式中觸發訊號。觸發訊號的過程後面再敘述,先看看訊號和槽具體是如何連線起來的。

QObject::connect有很多個過載的版本,這裡不一一介紹,上面呼叫的版本是
template <typename Func1, typename Func2>
QMetaObject::Connection connect(
    const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
    const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
    Qt::ConnectionType type = Qt::AutoConnection);
{
    typedef QtPrivate::FunctionPointer<Func1> SignalType;
    typedef QtPrivate::FunctionPointer<Func2> SlotType;

    // 轉交給 QMetaObject::Connection QObject::connectImpl(
    //       const QObject *sender, void **signal, const QObject *receiver, void **slot,
    //            QtPrivate::QSlotObjectBase *slotObj,
    //            Qt::ConnectionType type, const int *types,
    //            const QMetaObject *senderMetaObject)
    return connectImpl(sender, reinterpret_cast<void **>(&signal), receiver, reinterpret_cast<void **>(&slot),
        new QtPrivate::QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value, typename SignalType::ReturnType>(slot),
        type, nullptr,
        // 萃取出訊號所屬型別然後後獲得其QMetaObject,每個QObject型別或子型別都對應有一個QMetaObject物件
        &SignalType::Object::staticMetaObject);
}

很容看出這個函式做的實質性的工作有:

1、通過FunctionPointer的萃取實現在編譯期檢查訊號和傳送者是否屬於同一個類,接收者和槽是否屬於同一個類;

2、建立QSlotObject對slot進行封裝;

3、通過FunctionPointer的萃取獲得訊號所屬類的QMetaObject物件。

然後函式將任務轉交給connectImpl。

可以看到這個函式所做的工作不多,有些語句比較長。函式已經被刪減過,如果只考慮本例的話,為了更易於理解還可以進一步刪減,執行的過程如下:
template <typename Func1, typename Func2>
QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
    const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
    Qt::ConnectionType type = Qt::AutoConnection)
{
    return connectImpl(sender, (void **)(&signal), receiver, (void **)(&slot),
        new QtPrivate::QSlotObject<Func2, QtPrivate::List<>, void>(slot),
        type, nullptr, &Counter::staticMetaObject);
}

connectImpl的實現後面敘述,先看一下FunctionPointer的實現以及QSlotObject的作用。

FunctionPointer是一個模板類,沒有資料成員,有很多的特化版本,這裡只列舉和本例相關的兩個。(這個實現是在安裝路徑下的原始碼目錄中看到的,在vs2015直接按f12進去的實現用的是可變模板引數,和這裡的有點不一樣)
// 成員函式指標萃取類,真正有用的是特化版本,高超的設計!
// 這個設計可以實現編譯期檢查物件和成員函式是否屬於同一個類,也可以萃取成員函式的類型別、引數列表、引數個數和返回值型別,高!
template<typename Func>
struct FunctionPointer
{
    enum
    {
        ArgumentCount = -1,
        IsPointerToMemberFunction = false
    };
};

// FunctionPointer的特化版本,帶返回值型別
template<class Obj, typename Ret>
struct FunctionPointer<Ret(Obj::*) ()>
{
    typedef Obj Object;
    typedef void Arguments;
    typedef Ret ReturnType;
    // Function是無參返回Ret型別的成員函式指標型別
    typedef Ret(Obj::*Function) ();

    enum
    {
        ArgumentCount = 0,
        IsPointerToMemberFunction = true
    };

    template <typename Args, typename R>
    static void call(Function f, Obj *o, void **arg)
    {
        // 下面的","竟然是被過載的運算子,arg[0]存放返回值(如果有的話)
        (o->*f)(), ApplyReturnValue<R>(arg[0]);
    }
};

// FunctionPointer的特化版本,帶返回值型別和一個引數
template<class Obj, typename Ret, typename Arg1>
struct FunctionPointer<Ret(Obj::*) (Arg1)>
{
    typedef Obj Object;
    typedef List<Arg1, void> Arguments;
    typedef Ret ReturnType;
    // Function是帶一個引數返回Ret型別的成員函式指標型別
    typedef Ret(Obj::*Function) (Arg1);

    enum
    {
        ArgumentCount = 1, IsPointerToMemberFunction = true
    };

    template <typename Args, typename R>
    static void call(Function f, Obj *o, void **arg)
    {
        // 下面的","竟然是被過載的運算子,arg[0]存放返回值(如果有的話)
        (o->*f)((*reinterpret_cast<typename RemoveRef<typename Args::Car>::Type *>(arg[1]))), ApplyReturnValue<R>(arg[0]);
    }
};

// 可以巢狀 List<Arg1, List<Arg2, List<Arg3, void> > >,C++模板特色!
template <typename Head, typename Tail>
struct List
{
    typedef Head Car;
    typedef Tail Cdr;
};

/*
trick to set the return value of a slot that works even if the signal or the slot returns void
to be used like     function(), ApplyReturnValue<ReturnType>(&return_value)
if function() returns a value, the operator,(T, ApplyReturnValue<ReturnType>) is called, but if it
returns void, the builtin one is used without an error.
*/
template <typename T>
struct ApplyReturnValue
{
    void *data;
    explicit ApplyReturnValue(void *data_) : data(data_) {}
};
// 當T是void時便使用內建的逗號運算子,其它型別則呼叫下面的過載版本,有這麼好的事?
template<typename T, typename U>
void operator,(const T &value, const ApplyReturnValue<U> &container)
{
    if (container.data)
        *reinterpret_cast<U*>(container.data) = value;
}

FunctionPointer的實現用到了很多的模板和型別定義,但是並不難懂,主要的作用就是萃取。

先繞個彎子。Qt的訊號機制和一般的監聽者設計模式的功能類似,但實現上有些差別。監聽者設計模式的事件管理和事件觸發後通知監聽者的工作都是由被監聽者負責,而Qt的訊號槽連線的管理以及觸發訊號後執行相關的槽函式這些工作基本上集中在QObject中,同時配合元物件系統進行實現。(QObject集中管理訊號槽並不是程式所有的訊號槽連線都放在一起,而是功能由QObject類統一實現,但是每個QObject類或QObject子類的物件各自維護和自己相關的訊號槽連線,後面會有詳細的實現)

QObject需要集中管理訊號槽,但是QObject並不是模板類,而訊號或槽可能有非常多的型別,那麼如何將不同型別的訊號槽儲存在同一個資料結構中統一管理和使用呢?

Qt儲存訊號是首先將訊號轉換成int型別的index,然後就可以統一儲存和使用了,轉換和使用是通過QMetaObject::static_metacall方法進行的,QMetaObject::static_metacall的具體實現上面有。

Qt的槽比訊號更復雜,不能使用和訊號相同的方式進行處理,因為訊號一定是QObject類或QObject子類的成員函式,而槽可能是成員函式,也可能是靜態函式、lambda表示式、函式物件。Qt使用QSlotObjectBase類的子類對槽進行封裝,雖然子類都是模板類,並且有多個子類,但也都屬於QSlotObjectBase型別,這樣不同型別的槽就統一封裝成一種型別了。QSlotObjectBase有多個子類,實現大體相似,這裡只列舉成員函式槽QSlotObject一種。

// QSlotObjectBase共有3個實現類QSlotObject、QStaticSlotObject、QFunctorSlotObject,
// 分別對應成員函式、靜態函式、函式物件(或者lambda)3種槽。
// QSlotObjectBase將destroyIfLastRef、compare、call強行組合在m_impl,然後在子類以static形式實現,
// 而且QSlotObjectBase的解構函式是protected的,不能例項化,
// 為什麼不直接搞幾個protected抽象方法讓子類實現?
// internal base class (interface) containing functions required to call a slot managed by a pointer to function.
class QSlotObjectBase
{
    // 引用計數,不使用智慧指標,難道是因為智慧指標不保證多執行緒安全?
    QAtomicInt m_ref;
    // don't use virtual functions here; we don't want the
    // compiler to create tons of per-polymorphic-class stuff that
    // we'll never need. We just use one function pointer.
    typedef void(*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret);
    const ImplFn m_impl;
protected:
    enum Operation
    {
        Destroy,
        Call,
        Compare,

        // 當所有列舉都不指定值時,最後一個列舉的值就是前面列舉的數量,有意思!
        NumOperations
    };
public:
    // 這個m_impl通過建構函式傳入,並在destroyIfLastRef、compare、call各處回撥,
    // m_impl有點像鉤子方法,在子類QSlotObject中實現,有點模板方法設計模式的意思!
    explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {}

    inline int ref() noexcept
    {
        return m_ref.ref();
    }

    inline void destroyIfLastRef() noexcept
    {
        if (!m_ref.deref()) // 引用計數為0,銷燬自己
            m_impl(Destroy, this, nullptr, nullptr, nullptr);
    }

    inline bool compare(void **a)
    {
        bool ret;
        m_impl(Compare, this, nullptr, a, &ret);
        return ret;
    }

    inline void call(QObject *r, void **a)
    {
        m_impl(Call, this, r, a, nullptr);
    }
protected:
    // 解構函式是protected的,意思是必須通過子類使用?
    ~QSlotObjectBase() {}
private:
    // 阻止複製
    QSlotObjectBase(const QSlotObjectBase &) = delete;
    QSlotObjectBase &operator=(const QSlotObjectBase &) = delete;
};

// implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject
// Args and R are the List of arguments and the returntype of the signal to which the slot is connected.
template<typename Func, typename Args, typename R>
class QSlotObject : public QSlotObjectBase
{
    typedef QtPrivate::FunctionPointer<Func> FuncType;
    Func function;
    static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
    {
        switch (which) {
        case Destroy:
            delete static_cast<QSlotObject*>(this_);
            break;
        case Call:
            FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);
            break;
        case Compare:
            *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function;
            break;
        case NumOperations:;
        }
    }
public:
    explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
};

從上面可以總結出QSlotObjectBase類和它的子類的作用:

1、提供引用計數,管理自身資源;

2、比較兩個槽是否相等;

3、通過call可以呼叫槽函式;

4、將不同的子型別、模板型別統一成QSlotObjectBase型別,便於儲存訊號槽連線的資料結構統一管理。

接下來繼續上面的思路,看一下QObject::connectImpl的實現
// 這裡signal和slot為何是void **而不是void *?難道打算將修改它們的值?
QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
    const QObject *receiver, void **slot,
    QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
    const int *types, const QMetaObject *senderMetaObject)
{
    int signal_index = -1;
    void *args[] = { &signal_index, signal };
    for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) {
        // 獲取訊號的id,同一個類內(不包括父類)不同的訊號有不同的id
        senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
        // 訊號存在則退出,否則繼續在父類中尋找
        if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount)
            break;
    }
    // 在id基礎上再加上偏移量,偏移量是所有父類signal數量之和,這樣每個signal在類內(包括父類)的signal_index就唯一了
    signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
    return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);
}

/*!
Returns the signal offset for the class \a m; i.e., the index position
of the class's first signal.
*/
int QMetaObjectPrivate::signalOffset(const QMetaObject *m)
{   // 計算出所有父類的signal數量之和
    int offset = 0;
    for (m = m->d.superdata; m; m = m->d.superdata)
        offset += reinterpret_cast<const QMetaObjectPrivate*>(m->d.data)->signalCount;
    return offset;
}

上面函式QObject::connectImpl的功能:獲取signal在類內(包括父類)的唯一標號signal_index。

QObject::connectImpl函式已經被刪減過,如果只考慮本例的話,為了更易於理解還可以進一步刪減,執行的過程如下:
QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
    const QObject *receiver, void **slot,
    QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
    const int *types, const QMetaObject *senderMetaObject)
{
    int signal_index = -1;
    void *args[] = { &signal_index, signal };
    // 獲取訊號的id,同一個類內(不包括父類)不同的訊號有不同的id
    senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
    // 在id基礎上再加上所有父類signal數量之和,這樣每個signal在類內(包括父類)的signal_index就唯一了
    signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
    return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);
}
在獲取了signal在類內的唯一標號signal_index之後,執行QObjectPrivate::connectImpl,開始真正地將訊號和槽之間的連線儲存到特定的資料結構中。在介紹QObjectPrivate::connectImpl實現之前,先介紹一下Qt儲存訊號和槽連線使用的資料結構。首先是儲存訊號槽連線的實體Connection:
// 儲存訊號槽連線資訊的實體,同時也是單鏈表的節點
struct Connection
{
    QObject *sender;
    QObject *receiver;
    QtPrivate::QSlotObjectBase *slotObj;
    // The next pointer for the singly-linked ConnectionList
    Connection *nextConnectionList;

    // 下面4個是位域,參見《C++ Primer》第五版19.8.1
    uint signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())
    ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
    ushort isSlotObject : 1;
    ushort ownArgumentTypes : 1;
};

可以看到裡面封裝了一些關鍵的資訊,傳送者、接收者、訊號index和槽QSlotObjectBase,同時也有nextConnectionList指向下一個節點,說明這會是單鏈表結構。

Qt將屬於同一個訊號的Connection組織成一個單鏈表,然後將這些連結串列的表頭放到一個支援隨機訪問的順序容器中。從上面的敘述中可以瞭解到訊號首先被轉換成了int型的signal_index,使用signal_index作為索引就可以直接訪問到屬於該訊號的Connection連結串列。Connection連結串列表頭是struct ConnectionList,存放表頭的容器是class QobjectConnectionListVector,下面是ConnectionList定義以及它們之間的關係圖:
// ConnectionList is a singly-linked list
struct ConnectionList {
    ConnectionList() : first(0), last(0) {}
    Connection *first;
    Connection *last;
};

這些資料結構並不是直接在QObject類中定義,QObject通過QObjectPrivate管理這些內容,下面給出QObjectPrivate的刪減版定義。
class QObjectPrivate : public QObjectData
{
public:
    struct Connection
    {
        QObject *sender;
        QObject *receiver;
        QtPrivate::QSlotObjectBase *slotObj;
        // The next pointer for the singly-linked ConnectionList
        Connection *nextConnectionList;

        // 下面4個是位域,參見《C++ Primer》第五版19.8.1
        uint signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())
        ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
        ushort isSlotObject : 1;
        ushort ownArgumentTypes : 1;
    };

    struct ConnectionList {
        ConnectionList() : first(0), last(0) {}
        Connection *first;
        Connection *last;
    };

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

    static QObjectPrivate *get(QObject *obj) {
        // 獲取屬於obj的QObjectPrivate物件
        return obj->d_func();
    }

    static QMetaObject::Connection connectImpl(const QObject *sender, int signal_index,
        const QObject *receiver, void **slot,
        QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
        const int *types, const QMetaObject *senderMetaObject);
public:
    QObjectConnectionListVector *connectionLists;
    Connection *senders; // linked list of connections connected to this object
};
從上面很容易看出,確實是QObjectPrivate在維護訊號槽連線的資料結構,並且從static QObjectPrivate *get函式中可以看出,從QObject物件中能獲取QObjectPrivate例項。接下來看看QObjectPrivate::connectImpl將訊號槽連線加入資料結構中的過程。
QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
    const QObject *receiver, void **slot,
    QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
    const int *types, const QMetaObject *senderMetaObject)
{
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

    if (type & Qt::UniqueConnection)
    {
        // 獲取儲存訊號槽連線的線性表,QObjectPrivate::get(s)返回的是QObjectPrivate型別
        QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
        if (connectionLists && connectionLists->count() > signal_index)
        {   // 訊號存在於線性表中
            // QObjectPrivate::Connection是一個連結串列結構
            const QObjectPrivate::Connection *c2 = (*connectionLists)[signal_index].first;

            while (c2)
            {
                if (c2->receiver == receiver && c2->isSlotObject && c2->slotObj->compare(slot))
                {   // slot已和訊號連線,銷燬slotObj,返回連線失敗
                    slotObj->destroyIfLastRef();
                    return QMetaObject::Connection();
                }
                // 繼續連結串列的下一個節點
                c2 = c2->nextConnectionList;
            }
        }
        // Qt::UniqueConnection這個屬性是為了防止重複新增相同的訊號槽連線,
        // 如果不存在相同的連線則去掉這個屬性,也就是儲存訊號槽連線的資料結構並不存該屬性,
        // 該屬性只作為是否要新增連線的控制訊號。
        type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
    }

    // QScopedPointer的作用類似於智慧指標,詳情見Qt助手
    QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
    c->sender = s;
    c->signal_index = signal_index;
    c->receiver = r;
    c->slotObj = slotObj;
    c->connectionType = type;
    c->isSlotObject = true;

    // 新增新的訊號槽連線,QObjectPrivate::get(s)返回的是QObjectPrivate型別
    QObjectPrivate::get(s)->addConnection(signal_index, c.data());
    QMetaObject::Connection ret(c.take());

    QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index);
    s->connectNotify(method);

    return ret;
}
QObjectPrivate::connectImpl函式已經被刪減過,如果只考慮本例的話,為了更易於理解還可以進一步刪減,執行的過程如下:
QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
    const QObject *receiver, void **slot,
    QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
    const int *types, const QMetaObject *senderMetaObject)
{
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

    // QScopedPointer的作用類似於智慧指標,詳情見Qt助手
    QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
    c->sender = s;
    c->signal_index = signal_index;
    c->receiver = r;
    c->slotObj = slotObj;
    c->connectionType = type;
    c->isSlotObject = true;

    // 新增新的訊號槽連線,QObjectPrivate::get(s)返回的是QObjectPrivate型別
    QObjectPrivate::get(s)->addConnection(signal_index, c.data());
    QMetaObject::Connection ret(c.take());

    return ret;
}

可以瞭解到QObjectPrivate::connectImpl函式所做的工作有:

1、如果連線的型別屬於Qt::UniqueConnection,則首先檢查相同的訊號槽連線是否已存在,如果存在則返回連線失敗;

2、將傳送者、接收者、訊號、槽等等的資訊封裝在QObjectPrivate::Connection物件中,並交給QObjectPrivate::addConnection處理;

3、觸發新的訊號槽連線新增事件(s->connectNotify(method);)。

最後看看QObjectPrivate::addConnection過程:
/*
This vector contains the all connections from an object.

Each object may have one vector containing the lists of
connections for a given signal. The index in the vector correspond
to the signal index. The signal index is the one returned by
QObjectPrivate::signalIndex (not QMetaObject::indexOfSignal).
Negative index means connections to all signals.

This vector is protected by the object mutex (signalSlotMutexes())

Each Connection is also part of a 'senders' linked list. The mutex
of the receiver must be locked when touching the pointers of this
linked list.
*/
class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList>
{
public:
    bool orphaned; //the QObject owner of this vector has been destroyed while the vector was inUse
    bool dirty; //some Connection have been disconnected (their receiver is 0) but not removed from the list yet
    int inUse; //number of functions that are currently accessing this object or its connections
    QObjectPrivate::ConnectionList allsignals; // 接收所有訊號的連線

    QObjectConnectionListVector()
        : QVector<QObjectPrivate::ConnectionList>(), orphaned(false), dirty(false), inUse(0)
    { }

    QObjectPrivate::ConnectionList &operator[](int at)
    {
        if (at < 0)
            return allsignals;
        return QVector<QObjectPrivate::ConnectionList>::operator[](at);
    }
};

// 每個QObject裡通過QObjectPrivate維護connectionLists,senders
/*!
\internal
Add the connection \a c to the list of connections of the sender's object
for the specified \a signal

The signalSlotLock() of the sender and receiver must be locked while calling
this function

Will also add the connection in the sender's list of the receiver.
*/
void QObjectPrivate::addConnection(int signal, Connection *c)
{
    // connectionLists是QObjectPrivate中的屬性
    // 定義:QObjectConnectionListVector *connectionLists;
    if (!connectionLists)
        connectionLists = new QObjectConnectionListVector();
    // 如果signal不在connectionLists中,新增signal
    if (signal >= connectionLists->count())
        connectionLists->resize(signal + 1); // 擴容即是新增<=signal的所有元素

                                             // 獲取儲存訊號為signal連線的單鏈表
    ConnectionList &connectionList = (*connectionLists)[signal];
    // 將新節點c新增到連結串列中
    if (connectionList.last)
    {
        connectionList.last->nextConnectionList = c;
    }
    else
    {
        connectionList.first = c;
    }
    connectionList.last = c;

    cleanConnectionLists();

    // senders是QObjectPrivate中的屬性:Connection *senders,
    // 可能給自己傳送訊號的Connection單鏈表。
    // QObjectPrivate::get(s)返回的是QObjectPrivate型別
    c->prev = &(QObjectPrivate::get(c->receiver)->senders);
    // 將c新增到receiver的senders連結串列中
    c->next = *c->prev;
    *c->prev = c;
    if (c->next)
        c->next->prev = &c->next;
}

// 使用disconnect刪除Connection時並不真正在connectionLists中移除,
// 只是將Connection的receiver置為空,作為刪除標記,
// 所以在新增Connection之前需要從connectionLists移除有刪除標記的Connection。
// 找到節點的情況下,在連結串列中刪除它是O(1)的複雜度,不知道為什麼Qt在這裡使用延遲刪除,
// 可能是因為延遲刪除在disconnect時就不需要操作connectionlist資料結構,
// 這樣的話就只需要在新增connection的過程中考慮操作connectionlist的同步問題了
// (addconnection裡原來是有用於執行緒同步的程式碼,這裡為了簡化問題已將其刪除)
void QObjectPrivate::cleanConnectionLists()
{
    if (connectionLists->dirty && !connectionLists->inUse) {
        // remove broken connections
        for (int signal = -1; signal < connectionLists->count(); ++signal) {
            QObjectPrivate::ConnectionList &connectionList =
                (*connectionLists)[signal];

            // Set to the last entry in the connection list that was *not*
            // deleted.  This is needed to update the list's last pointer
            // at the end of the cleanup.
            QObjectPrivate::Connection *last = 0;

            QObjectPrivate::Connection **prev = &connectionList.first;
            QObjectPrivate::Connection *c = *prev;
            while (c) {
                if (c->receiver) {
                    last = c;
                    prev = &c->nextConnectionList;
                    c = *prev;
                }
                else {
                    QObjectPrivate::Connection *next = c->nextConnectionList;
                    *prev = next;
                    c->deref();
                    c = next;
                }
            }

            // Correct the connection list's last pointer.
            // As conectionList.last could equal last, this could be a noop
            connectionList.last = last;
        }
        connectionLists->dirty = false;
    }
}
addConnection過程已經被刪減過,如果只考慮本例的話,為了更易於理解還可以進一步刪減,執行的過程如下:
/*
This vector contains the all connections from an object.
*/
class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList>
{
};

/*!
Add the connection \a c to the list of connections of the sender's object
for the specified \a signal
*/
void QObjectPrivate::addConnection(int signal, Connection *c)
{
    // connectionLists是QObjectPrivate中的屬性
    // 定義:QObjectConnectionListVector *connectionLists;
    if (!connectionLists)
        connectionLists = new QObjectConnectionListVector();
    // 如果signal不在connectionLists中,新增signal
    if (signal >= connectionLists->count())
        connectionLists->resize(signal + 1); // 擴容即是新增<=signal的所有元素

                                             // 獲取儲存訊號為signal連線的單鏈表
    ConnectionList &connectionList = (*connectionLists)[signal];
    // 將新節點c新增到連結串列中
    if (connectionList.last)
    {
        connectionList.last->nextConnectionList = c;
    }
    else
    {
        connectionList.first = c;
    }
    connectionList.last = c;
}

訊號的觸發和執行

首先回顧一下上面的例子,main函式執行了c.count();之後,訊號是在count函式裡觸發的:

void Counter::count()
{
    ++m_value;
    emit valueChanged(m_value);
}
emitvalueChanged(m_value);語句傳送了訊號,在這個語句下具體執行了哪些操作?首先看一下發送訊號關鍵字emit的定義,emit是一個巨集,定義如下:
# define emit
可以看到,這個巨集是空的,只是一個標記,所以emit valueChanged(m_value);這個語句只是執行了valueChanged函式而已。從前面的敘述中可知valueChanged由元物件編譯器在moc_main.cpp已實現:
// SIGNAL 0
void Counter::valueChanged(int _t1)
{
    // _a[0]是返回值存放地址,這裡返回型別為void,所以是nullptr
    void *_a[] = { nullptr, &_t1 };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
這個函式將返回值和引數放到數組裡就直接交給QMetaObject::activate了。
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
    void **argv)
{
    activate(sender, QMetaObjectPrivate::signalOffset(m), local_signal_index, argv);
}

// 執行所有和訊號連線的函式或可呼叫物件
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{
    // 在local_signal_index基礎上再加上偏移量(所有父類signal數量之和),
    // 這樣每個signal在類內(包括父類)的signal_index就唯一了
    int signal_index = signalOffset + local_signal_index;

    void *empty_argv[] = { 0 };

    // 區域性類,管理QObjectConnectionListVector的inUse欄位
    struct ConnectionListsRef {
        QObjectConnectionListVector *connectionLists;
        ConnectionListsRef(QObjectConnectionListVector *connectionLists) : connectionLists(connectionLists)
        {
            if (connectionLists)
                ++connectionLists->inUse;
        }
        ~ConnectionListsRef()
        {
            if (!connectionLists)
                return;

            --connectionLists->inUse;
            if (connectionLists->orphaned) {
                if (!connectionLists->inUse)
                    delete connectionLists;
            }
        }

        QObjectConnectionListVector *operator->() const { return connectionLists; }
    };

    // sender->d_func()返回QObjectPrivate型別
    ConnectionListsRef connectionLists = sender->d_func()->connectionLists;

    // 獲取屬於signal的Connection連結串列
    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 {
            // c->receiver為空的話說明Connection已被標記為刪除
            if (!c->receiver)
                continue;

            QObject * const receiver = c->receiver;

            QConnectionSenderSwitcher sw;

            const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;
            const int method_relative = c->method_relative;
            if (c->isSlotObject) {  // 連線的是槽函式
                c->slotObj->ref();
                // QScopedPointer的作用類似於智慧指標,詳情見Qt助手
                QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);

                // 呼叫slotObj->call方法,即執行槽函式
                obj->call(receiver, argv ? argv : empty_argv);

                obj.reset();

            }
            else if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
                const int methodIndex = c->method();

                callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);
            }
            else {
                const int method = method_relative + c->method_offset;

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

            if (connectionLists->orphaned)
                break;
        } while (c != last && (c = c->nextConnectionList) != 0);

        if (connectionLists->orphaned)
            break;
        // 這個迴圈最多進行兩次,list分別為
        // &connectionLists->at(signal_index)和&connectionLists->allsignals)
    } while (list != &connectionLists->allsignals &&
        //start over for all signals;
        ((list = &connectionLists->allsignals), true));
}
QMetaObject::activate函式已經被刪減過,如果只考慮本例的話,為了更易於理解還可以進一步刪減,執行的過程如下:
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
    void **argv)
{
    activate(sender, QMetaObjectPrivate::signalOffset(m), local_signal_index, argv);
}

// 執行所有和訊號連線的函式或可呼叫物件
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{
    // 在local_signal_index基礎上再加上偏移量(所有父類signal數量之和),
    // 這樣每個signal在類內(包括父類)的signal_index就唯一了
    int signal_index = signalOffset + local_signal_index;

    // sender->d_func()返回QObjectPrivate型別
    QObjectConnectionListVector connectionLists = sender->d_func()->connectionLists;

    // 獲取屬於signal的Connection連結串列
    const QObjectPrivate::ConnectionList *list;
    list = &connectionLists->at(signal_index);

    do {
        QObject * const receiver = c->receiver;
        c->slotObj->ref();
        // QScopedPointer的作用類似於智慧指標,詳情見Qt助手
        QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
        // 呼叫slotObj->call方法,即執行槽函式
        obj->call(receiver, argv);
        obj.reset();
    } while (c != last && (c = c->nextConnectionList) != 0);
}