信号槽通过connect进行连接,connect的源码在qobject.cpp中

QT4中的connect的声明如下

static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);

使用时,一般是这样的

connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));

其中的SIGNAL和SLOT定义如下,在qobjectdefs.h中

# define SLOT(a)     "1"#a
# define SIGNAL(a)   "2"#a

#a 得到参数的字符串形式,对于槽函数的宏,前面加 "1",对于信号的宏,前面加 "2",则上面的connect代码就变成如下形式

connect(sender, "2destroyed()", this, "1objectDestroyed()");

connect具体实现如下

QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,const QObject *receiver, const char *method,Qt::ConnectionType type)
{if (sender == nullptr || receiver == nullptr || signal == nullptr || method == nullptr) {qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",sender ? sender->metaObject()->className() : "(nullptr)",(signal && *signal) ? signal+1 : "(nullptr)",receiver ? receiver->metaObject()->className() : "(nullptr)",(method && *method) ? method+1 : "(nullptr)");return QMetaObject::Connection(0);}//检查sender、receiver、signal、method是否为空,为空,返回个空连接QByteArray tmp_signal_name;if (!check_signal_macro(sender, signal, "connect", "bind"))//检查signal字符串中是否有2,如果没有,空连接返回return QMetaObject::Connection(0);const QMetaObject *smeta = sender->metaObject();const char *signal_arg = signal;++signal; //跳过2,指向具体的字符串QArgumentTypeArray signalTypes;Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName, signalTypes.size(), signalTypes.constData());//计算信号的相对序号if (signal_index < 0) {// check for normalized signaturestmp_signal_name = QMetaObject::normalizedSignature(signal - 1);signal = tmp_signal_name.constData() + 1;signalTypes.clear();signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);smeta = sender->metaObject();signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName, signalTypes.size(), signalTypes.constData());}//如果相对序号不能存在,normalized后,再计算一次信号函数的相对序号if (signal_index < 0) {err_method_notfound(sender, signal_arg, "connect");err_info_about_objects("connect", sender, receiver);return QMetaObject::Connection(0);}//如果normalized后,信号函数还不存在,返回空连接signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);signal_index += QMetaObjectPrivate::signalOffset(smeta);//将signal_index加上偏移,变为绝对序号QByteArray tmp_method_name;int membcode = extract_code(method);if (!check_method_code(membcode, receiver, method, "connect"))return QMetaObject::Connection(0);//检查槽函数是否有字符1const char *method_arg = method;++method; //处理方法同信号QArgumentTypeArray methodTypes;QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);const QMetaObject *rmeta = receiver->metaObject();int method_index_relative = -1;Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);switch (membcode) {//根据membcode是1还是2计算槽函数的相对序号,因为信号可以连接信号,所以在考虑槽函数的时候要处理槽函数是信号的情况case QSLOT_CODE:method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;case QSIGNAL_CODE:method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;}if (method_index_relative < 0) {//如果得到的槽函数的相对序号不合法,normalized并再计算槽函数的相对序号// check for normalized methodstmp_method_name = QMetaObject::normalizedSignature(method);method = tmp_method_name.constData();methodTypes.clear();methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);// rmeta may have been modified abovermeta = receiver->metaObject();switch (membcode) {case QSLOT_CODE:method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;case QSIGNAL_CODE:method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;}}if (method_index_relative < 0) {//再次判断,如果槽函数的序号还是不合法的,返回空连接err_method_notfound(receiver, method_arg, "connect");err_info_about_objects("connect", sender, receiver);return QMetaObject::Connection(0);}if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),methodTypes.size(), methodTypes.constData())) {qWarning("QObject::connect: Incompatible sender/receiver arguments""\n        %s::%s --> %s::%s",sender->metaObject()->className(), signal,receiver->metaObject()->className(), method);return QMetaObject::Connection(0);}//检查信号函数和槽函数的参数是否兼容,不兼容,返回空连接int *types = 0;if ((type == Qt::QueuedConnection)&& !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {return QMetaObject::Connection(0);}//如果此时信号槽的连接方式是QueuedConnection,但是queuedConnectionTypes的返回值为空(信号的类型数据没有获取到),那么也返回一个空连接#ifndef QT_NO_DEBUGQMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endifQMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));//前面的check都没问题,进行连接,调用QMetaObjectPrivate::connectreturn handle;
}

QMetaObjectPrivate::connect也在qobject.cpp中

QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,int signal_index, const QMetaObject *smeta,const QObject *receiver, int method_index,const QMetaObject *rmeta, int type, int *types)
//sender 是发送信号的对象;signal_index 信号绝对序号;smeta 是发送信号的对象的元对象;
//receiver 是信号的接收对象;method_index 是槽函数的相对序号;rmeta 是信号的接收对象的元对象;
//type 是连接类型;types 是指向的是入队时,信号的类型数据。
{QObject *s = const_cast<QObject *>(sender);QObject *r = const_cast<QObject *>(receiver);//去底层constint method_offset = rmeta ? rmeta->methodOffset() : 0;//加上偏移量之后,得到槽函数的绝对序号Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : nullptr;//函数指针指向接收端元对象的qt_static_metacall(在MOC文件中实现)QOrderedMutexLocker locker(signalSlotLock(sender),signalSlotLock(receiver));//互斥锁,为了多线程顺序访问QObjectPrivate::ConnectionData *scd  = QObjectPrivate::get(s)->connections.loadRelaxed();//获取sender的ConnectionDataif (type & Qt::UniqueConnection && scd) {//如果连接类型有UniqueConnection并且sender的ConnectionData有数据if (scd->signalVectorCount() > signal_index) {//判断信号的绝对索引小于信号的数量const QObjectPrivate::Connection *c2 = scd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();int method_index_absolute = method_index + method_offset;while (c2) {if (!c2->isSlotObject && c2->receiver.loadRelaxed() == receiver && c2->method() == method_index_absolute)return nullptr;c2 = c2->nextConnectionList.loadRelaxed();}//遍历ConnectionList,如果发现ConnectionList中的receiver和传入的receiver相等并且该connection中的方法索引和绝对索引相等,那么就认为不满足UniqueConnection,直接退出}type &= Qt::UniqueConnection - 1;}std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};//创建一个QObjectPrivate::Connection并对其中的数据进行赋值c->sender = s;c->signal_index = signal_index;c->receiver.storeRelaxed(r);QThreadData *td = r->d_func()->threadData;td->ref();c->receiverThreadData.storeRelaxed(td);c->method_relative = method_index;c->method_offset = method_offset;c->connectionType = type;c->isSlotObject = false;c->argumentTypes.storeRelaxed(types);c->callFunction = callFunction;QObjectPrivate::get(s)->addConnection(signal_index, c.get());//根据信号的绝对序号,添加QObjectPrivate::Connection对象locker.unlock();//解锁QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);if (smethod.isValid())s->connectNotify(smethod);return c.release();
}

上述代码中的loadrelaxed调用的其实就是C++11中的atomic类中的load方法,返回的是其中类或结构中包含的数据,详细见http://www.cplusplus.com/reference/atomic/atomic/load/

addConnection实现如下

void QObjectPrivate::addConnection(int signal, Connection *c)
{Q_ASSERT(c->sender == q_ptr);ensureConnectionData();ConnectionData *cd = connections.loadRelaxed();cd->resizeSignalVector(signal + 1);//为新信号的Connectionlist分配内存ConnectionList &connectionList = cd->connectionsForSignal(signal);//找到信号的connectionListif (connectionList.last.loadRelaxed()) {//添加新的信号的connectionlistQ_ASSERT(connectionList.last.loadRelaxed()->receiver.loadRelaxed());connectionList.last.loadRelaxed()->nextConnectionList.storeRelaxed(c);} else {connectionList.first.storeRelaxed(c);}c->id = ++cd->currentConnectionId;//设置新的connection的idc->prevConnectionList = connectionList.last.loadRelaxed();//设置新的connection的prevConnectionListconnectionList.last.storeRelaxed(c);QObjectPrivate *rd = QObjectPrivate::get(c->receiver.loadRelaxed());rd->ensureConnectionData();c->prev = &(rd->connections.loadRelaxed()->senders);c->next = *c->prev;//将新的connection添加到对应的信号的connectionlist中*c->prev = c;if (c->next)c->next->prev = &c->next;
}

connection与connectionlist的整体的结构图如下

参考

Qt5.14源码

https://qtguide.ustclug.org/

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

4.QT4中的connect的实现相关推荐

  1. 5.QT5中的connect的实现

    在QT4中,解析信号槽是通过将信号槽的名字转化为字符串,然后通过connect解析该字符串,得到信号函数的相对序号和,然后创建信号connectionlist,但是,所有的检查都在运行时,通过解析字符 ...

  2. ORA-01436: 用户数据中的CONNECT BY 循环

    起始地     目的地     距离(公里) A             B             1000 A             C             1100 A           ...

  3. 在Qt4中使用QPersistentModelIndex传递QModelIndex

    在Qt4的model/view架构中,用的非常多的是QModelIndex,但这个类是动态的,可以说不可以传递的,因为随着view的变化,相同结点的QModelIndex也可能不同,非常容易失效,甚至 ...

  4. ITK:在二进制映像中标记Connect组件

    ITK:在二进制映像中标记Connect组件 内容提要 C++实现代码 内容提要 在二进制映像中标记连接的组件. C++实现代码 #include "itkLiThresholdImageF ...

  5. QT3与QT4中uic的使用差异---李家凯老师

    1.  QT3 在QT3中,用designer设计好界面后,使用uic来根据界面生成代码文件,QT3的uic可以自动生成.h和.cpp文件,假设你在制作一个名叫DemoDlg的对话框,生成文件名为De ...

  6. 数据库异常---ORA-01436: 用户数据中的 CONNECT BY loop in user data 循环

    数据库 ORA-01436: 用户数据中的 CONNECT BY  loop in user data  循环 技术qq交流群:JavaDream:251572072  教程下载,在线交流:创梦IT社 ...

  7. QT下信号与槽不在同一个线程中如何connect

    QT下信号与槽不在同一个线程中如何connect

  8. react-redux中的connect

    将redux中的state与react中组件中的属性或者方法作绑定. 1.createConnect 作为高阶函数,创建Connect. function createConnect({connect ...

  9. UDP socket编程中使用connect

    转自:http://hi.baidu.com/rwen2012/item/545a39ba741307d085dd7957 标准的udp客户端开了套接口后,一般使用sendto和recvfrom函数来 ...

最新文章

  1. c语言编程 构建围墙,c语言程序设计朝盛 综合程序练习题.ppt
  2. 首例基因编辑干细胞治疗艾滋病:北大邓宏魁参与,达到最佳治疗效果
  3. Redis笔记之常用命令
  4. 这是一位川大零基础转行 Python 的人生勇士
  5. 【ArcGIS风暴】ArcGIS中制作GPS点位轨迹线及多边形
  6. 前端学习(2971):静态打包资源
  7. 挣值管理:PV,AC和EV
  8. 用R语言实现密度聚类dbscan
  9. 如何在工作中快速成长?阿里资深架构师给工程师的10个简单技巧
  10. Inceptor上存储过程相关
  11. Mike and Cellphone
  12. 企业微信集成自建应用——踩坑记录
  13. 命令行里打 cd 简直是浪费生命
  14. 视频教程-微信公众平台深度开发v2.0第3季——二维码、模板消息-微信开发
  15. WPf 带滚动条WrapPanel 自动换行 和控件右键菜单
  16. java制作坦克大战
  17. Nanopi NEO Core测试
  18. php 模拟登录qq空间,PHP模拟登录QQ空间的例子
  19. 计算机网络与协议分析,计算机网络-使用网络协议分析器捕捉和分析协议数据包...
  20. 一、slowfast 代码复现

热门文章

  1. 网路游侠:日志审计系统与SOC的区别
  2. 使用pgpool-ii建立PostgreSQL链接池
  3. [android] androidPN开源项目介绍
  4. Supporting Python 3(支持python3)——常见的迁移问题
  5. Parallel并行计算合计数据时错误的原因和解决办法
  6. 软件项目开发无成熟框架套路之成本代价
  7. MATLAB与Python numpy矩阵操作对应表
  8. 基于SSM实现个人博客系统
  9. Tableau 绘制折线图
  10. 一家专业做SEO的公司介绍给大家|利槿网络