QObject的Connect方法

本文深入分析了Qt中QObject的Connect方法实现原理,包括信号和槽的宏定义转换过程,以及connect函数内部如何通过回调机制建立信号与槽之间的连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

QObjectConnect方法

前两天分析了元对象系统的中,元对象编译器所做的部分,进过分析后,我们可以清楚的看到,实际上元对象编译器做了很简单的一个工作,甚至我们可以自己写一个程序来对这一部分代码进行优化。

对于Qt生成的代码,我最不满意的地方就是那个通过switch-case进行信号和槽的选择调用,速度好慢的,不知道能否采用其他方式进行一定得优化。

言归正传,我们开始今天的分析,关于connect函数的实现。要想了解connect的实现首先要看参数是怎么传递的:

信号的函数和槽的函数通过两个宏定义(SIGNAL,SLOT)完成了一次转换,具体是怎么做的呢?我们看下这两个宏的定义:

Q_CORE_EXPORT const char *qFlagLocation(const char *method);

#define QTOSTRING_HELPER(s) #s

#define QTOSTRING(s) QTOSTRING_HELPER(s)

#ifndef QT_NO_DEBUG

# define QLOCATION "/0"__FILE__":"QTOSTRING(__LINE__)

# define METHOD(a)   qFlagLocation("0"#a QLOCATION)

# define SLOT(a)     qFlagLocation("1"#a QLOCATION)

# define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)

#else

# define METHOD(a)   "0"#a

# define SLOT(a)     "1"#a

# define SIGNAL(a)   "2"#a

#endif

通过这个,我们可以看到,实际上SIGNALSLOT实现的方法是将函数名称转换为字符串。这里采用qDebug()输入转换后的参数,发现,实际上Qt只是为每个函数的名字前面加上了数字编号的0120表示普通方法,1表示槽,2表示信号。但是弄不明白的一个问题是,看似在函数转换的时候,如果没有定义了QT_NO_DEBUG的时候,Qt还添加了一些信息在里面,但是转换后的结果都是一个。

通过前面的一些分析,我们将宏替换掉,得到的真实代码是这样的:

qFlagLocation(“1Hello/0”__FILE__”:__LINE__”)

我用程序测试了一下这个代码,发现其真实的结果是:

__FILE__显示了当前文件的名称,__LINE__显示了当前指令所在的行数。

这样的写法是在Debug版本中的,在Release版本中的就只有”1Hello”了。接下来,我们看下qFlagLocation的实现:

const int flagged_locations_count = 2;

static const char* flagged_locations[flagged_locations_count] = {0};

const char *qFlagLocation(const char *method)

{

    static int idx = 0;

    flagged_locations[idx] = method;

    idx = (idx+1) % flagged_locations_count;

    return method;

}

这里保留了当前信号或者槽的名称变量地址。并没有对字符串做任何处理。

这样,我们明白了QtSIGNAL宏和SLOT宏产生的效果,接下来就是看connect函数的实现了。

        const void *cbdata[] = { sender, signal, receiver, method, &type };

        if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))

            return true;

这样,我们就可以清楚的看到,上面这段代码的含义了,将通过形参传递过来的变量的指针赋值到一个数组,然后调用了QInternal类中的activateCallbacks方法,在第一个参数那里,看到了ConnectCallback的字样,从字面的理解就是一个连接的回调函数。让我们进入这个函数,来一看究竟吧。

我们先看下QInternal类的声明:

class Q_CORE_EXPORT QInternal {

public:

    enum PaintDeviceFlags {

        UnknownDevice = 0x00,

        Widget        = 0x01,

        Pixmap        = 0x02,

        Image         = 0x03,

        Printer       = 0x04,

        Picture       = 0x05,

        Pbuffer       = 0x06,    // GL pbuffer

        FramebufferObject = 0x07, // GL framebuffer object

        CustomRaster  = 0x08,

        MacQuartz     = 0x09,

        PaintBuffer   = 0x0a,

        OpenGL        = 0x0b

    };

    enum RelayoutType {

        RelayoutNormal,

        RelayoutDragging,

        RelayoutDropped

    };

 

 

    enum Callback {

        ConnectCallback,

        DisconnectCallback,

        AdoptCurrentThread,

        EventNotifyCallback,

        LastCallback

    };

 

    enum InternalFunction {

        CreateThreadForAdoption,

        RefAdoptedThread,

        DerefAdoptedThread,

        SetCurrentThreadToMainThread,

        SetQObjectSender,

        GetQObjectSender,

        ResetQObjectSender,

        LastInternalFunction

    };

 

    enum DockPosition {

        LeftDock,

        RightDock,

        TopDock,

        BottomDock,

        DockCount

    };

 

    static bool registerCallback(Callback, qInternalCallback);

    static bool unregisterCallback(Callback, qInternalCallback);

 

    static bool activateCallbacks(Callback, void **);

    static bool callFunction(InternalFunction func, void **);

};

做了一些枚举数据,这里的类型还是比较熟悉的,之后是注册回到函数,撤销回调函数,激活回调函数等操作。

bool QInternal::registerCallback(Callback cb, qInternalCallback callback)

{

    if (cb >= 0 && cb < QInternal::LastCallback) {

        QInternal_CallBackTable *cbt = global_callback_table();

        cbt->callbacks.resize(cb + 1);

        cbt->callbacks[cb].append(callback);

        return true;

    }

    return false;

}

整体的实现不是很多,首先就是重新分配回调函数表的大小,之后将新的回调函数加入。

 

bool QInternal::unregisterCallback(Callback cb, qInternalCallback callback)

{

    if (cb >= 0 && cb < QInternal::LastCallback) {

        QInternal_CallBackTable *cbt = global_callback_table();

        return (bool) cbt->callbacks[cb].removeAll(callback);

    }

    return false;

}

 

撤销的实现也很简单,直接进行了删除操作。

 

bool QInternal::activateCallbacks(Callback cb, void **parameters)

{

    Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");

 

    QInternal_CallBackTable *cbt = global_callback_table();

    if (cbt && cb < cbt->callbacks.size()) {

        QList<qInternalCallback> callbacks = cbt->callbacks[cb];

        bool ret = false;

        for (int i=0; i<callbacks.size(); ++i)

            ret |= (callbacks.at(i))(parameters);

        return ret;

    }

    return false;

}

 

调用则是一个查找的过程,在找到之后,将函数指针对应的做调用即可。可以看出来,整个的过程,只是简单的函数指针调用。

这里还有对回调函数数据的存储方式设计:

struct QInternal_CallBackTable {

    QVector<QList<qInternalCallback> > callbacks;

};

这部分很简单,之后的会怎么样呢?才做了这枚一些简单的判定,已经到了必须睡觉的时候,今天只能到这样了。明天继续。

 

20091019日星期一 2357

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值