Qt信号槽机制源码学习

版权声明:本文为博主原创文章,转载请在文章开头给出原文链接。 https://blog.csdn.net/xiyanggudao/article/details/51579611

简述

这里并不全面介绍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);
}

猜你喜欢

转载自blog.csdn.net/xiyanggudao/article/details/51579611