Qt中的信号与槽机制/槽机制和C++语言的联系与区别

QT中的信号槽机制能否用boost::signal实现
[问题点数:20分]
本版专家分:0
结帖率 39.13%
CSDN今日推荐
本版专家分:6572
2010年3月 移动平台大版内专家分月排行榜第一
2010年2月 移动平台大版内专家分月排行榜第二
本版专家分:29337
2007年10月 总版技术专家分月排行榜第二
2007年11月 总版技术专家分月排行榜第三
2007年10月 VC/MFC大版内专家分月排行榜第一2007年9月 VC/MFC大版内专家分月排行榜第一2008年6月 移动平台大版内专家分月排行榜第一2008年4月 移动平台大版内专家分月排行榜第一2008年3月 移动平台大版内专家分月排行榜第一2008年1月 移动平台大版内专家分月排行榜第一2007年11月 移动平台大版内专家分月排行榜第一2007年10月 移动平台大版内专家分月排行榜第一2007年8月 移动平台大版内专家分月排行榜第一
2010年3月 移动平台大版内专家分月排行榜第二2008年8月 移动平台大版内专家分月排行榜第二2007年11月 VC/MFC大版内专家分月排行榜第二2008年2月 移动平台大版内专家分月排行榜第二2007年12月 移动平台大版内专家分月排行榜第二2007年9月 移动平台大版内专家分月排行榜第二
本版专家分:2209
本版专家分:3599
匿名用户不能发表回复!|
CSDN今日推荐扫一扫下载手机客户端
扫描我,关注团购信息,享更多优惠
||网络安全
| | | | | | | | | | | | | | | |
||电子电工
汽车交通| | | | | | | | | |
||投资理财
| | | | | | | | | | | | | | | | |
| | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
||外语考试
| | | | | | | | |
| 视频教程|
Qt中的C++技术
定价:¥55.00
校园优惠价:¥41.25 (75折)
促销活动:
此商品暂时缺货(可留下联系方式,到货将第一时间通知您)
如果您急需团购,可点击“团购急调”按钮将此书加入购物车,由客服人员为您协调调货!
电话号码:
*邮箱地址:
ISBN:8上架时间:出版日期:2012 年7月开本:16开页码:285版次:1-1
所属分类:
  国内首本深入剖析Qt设计理念及源代码的书籍
让我们从这《Qt中的C++技术》开始,一起体验C++处理复杂问题的强大及其精妙吧!
《Qt中的C++技术》剖析了开源开发框架Qt中的C++技术,给读者提供一个优秀的案例,以学习C++语言以及面向对象设计技术。该书讨论了以下内容:类模板特化技术;分析比较了C++标准库、Qt对字符串、数据输入/输出的处理思路;隐式共享与d-pointer技术;函子及其在QTL(Qt Template Library)中的应用,QTL是如何使用模板特化技术优化QList性能的;如何在C++程序中嵌入汇编代码,实现一个原子操作,以很小的开销实现线程间通信;信号与槽机制;Graphics/View框架等。
软件学院或者计算机学院的学生,可将《Qt中的C++技术》作为课程“C++程序设计”或者“面向对象软件设计”的参考书;上述课程的教师,可将《Qt中的C++技术》的内容融入他们的主讲或者试验环节,作为相关实训课程的教材;软件行业的开发者,可将《Qt中的C++技术》作为深入学习C++设计与编程技术的案例教材。
《Qt中的C++技术》
案例的选择与评估 1
案例的初步选择 1
案例的定量评估 3
其他案例 5
基本约定 6
关于类图的约定 8
Qt库的编译 14
开发环境的设置 16
主控台的输入与输出 18
Qt风格的编程规范 19
与Qt及C++相关的文献资源 21
类模板特化 24
类模板特化 24
Traits技术 27
类型分类(Type Classification)技术 28
降低代码膨胀 30
标准库及Qt对字符串的处理 32
  据2011年12月Tiobe网站(www.tiobe.com)的排名,最流行的前5个编程语言依次是Java、C、C++、C#以及Objective-C。排名的依据是熟练使用一种语言的人数,与该语言相关的课程数量以及支持该语言的第三方供应商的数量。自2001年这个排名标准诞生以来,C++几乎总是处于第3名。虽然Perl、Visual Basic以及PHP也曾占据过这个位置,但是它们只能在这个位置上维持几个月。
  软件行业的骨架是由C++(及其兄弟C)搭建起来的。
  三大桌面操作系统Windows,MacOS以及Chrome OS使用了C++。
  运行在苹果公司iPhone,iPod,iTouch以及iPad上的操作系统,Windows Mobile和Symbian OS使用了C++。
  在关系数据库管理系统方面,主流的产品Oracle Database,MySQL,IBM DB2,Microsoft SQL Server,IBM Informix,SAP DB/MaxDB都使用了C++。
  Web浏览器方面,依据网络分析公司 StatCounter 2011年11月发布的数据,全球用户数量最多的前5款浏览器依次为微软的IE,Google Chrome,Mozilla Firefox,Safari以及Opera,它们全部使用了C++。
  流行的办公套件Microsoft Office,Sun Open Office,Corel Office也都使用了C++,其中Corel Office在开发过程中曾使用过Java,但由于速度太慢最终转回C/C++。
  甚至,出乎一般人的预料,C++也被用来开发网站。Google的网站采用C++(及汇编),eBay和Amazon采用C++与Java,Facebook采用LAMP(Linux+Apache+ Mysql+PHP)外加C++。
  C++的语言特性
  C++的地位是由其鲜明的语言特性决定的。
  (1)它兼容C,意味着C++项目可以复用过去40年来积累的C函数库以及C源代码。
  (2)对复杂系统的抽象表达能力。C++和其他面向对象编程语言一样,使用“类”封装底层的数据和相关的操作,使用“继承”描述基类和派生类的共性,使用“多态性”描述不同子类的差异。一个复杂的系统总会被表示为一组具有清晰逻辑结构的类。为了解决复杂软件系统中名字冲突的问题,C++引入了函数名重载(function overloading)、名字空间(namespace)机制。
  (3)执行速度快。C++和其他面向对象编程语言不同,并不追求形式上的简单。为了提高程序执行速度,C++不惜将自己变得更加复杂。模板技术以及基于该技术的泛型编程思想在编译阶段处理多态性问题,使得一个类模板或者函数模板能够处理各种类型。而其他编程语言采用虚函数与多态性解决类似的问题,相比之下,C++模板的执行速度更快。
  (4)C++遵循“用时付费”原则。例如,有些编程语言将垃圾回收(gabage collection)作为语言内在的功能,无论一个程序是否需要,该机制在程序运行期间总会占用一定的计算资源。对于C++,即使演化到最新的C++0x11标准,它仍然坚持不纳入垃圾回收机制,因为它认为需要该功能的程序可以采用第三方库,而不需要该功能的程序不应花费任何额外计算资源。C++程序被编译为可执行代码后可以直接运行,不像其他一些语言那样需要一个虚拟机来解释执行。在对速度要求极高的场合,我们可以在C++程序中嵌入汇编代码。这些语言特性都能提高C++程序的执行速度。
  《Qt中的C++技术》特点
  然而,也正因为C++的这些语言特性,使其成为所有面向对象语言中最复杂的一个。许多学习者抱怨C++语言“难学难精”。为了降低学习难度,提高学习效率,C++学习者至少应该阅读两种类型的教材:
  讲述“C++是什么”的教材,如Thinking in C++以及 C++ Primer等。
  讲述“如何使用C++”的教材,如Effective C++以及The C++ Programming Language(每章的Advice部分与Part Ⅳ)。
  阅读第一种类型的教材时,学习者通常不会遇到什么障碍,然而在阅读第二种类型的教材时,学习者有时会觉得其中一些C++编程经验听起来有道理,但是在编程实践中并不一定能够灵活运用它们。有这种感觉并不奇怪,因为这些编程经验是C++高手们从几十年的编程实践中总结出来的,要真正理解它们,需要读者也能以某种方式重演这些经验背后的编程实践。
  一种最有效的方式就是选择一个优秀的C++案例,研读它、模仿它,也就是说,C++学习者还应该阅读第三种类型的教材:案例教材。
作者其它作品¥128.00¥96.00
同类热销商品¥35.00¥24.50
订单处理配送
北京奥维博世图书发行有限公司 china-pub,All Rights ReservedQT的信号与槽机制的详细介绍及范例_百度文库
您的浏览器Javascript被禁用,需开启后体验完整功能,
享专业文档下载特权
&赠共享文档下载特权
&10W篇文档免费专享
&每天抽奖多种福利
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
QT的信号与槽机制的详细介绍及范例
&&信号与槽作为 QT 的核心机制在 QT 编程中有着广泛的应用,本文介绍了信号与槽的一些基本概念、元对象工具以及在实际使用过程中应注意的一些问题。
阅读已结束,下载本文需要
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,同时保存到云知识,更方便管理
加入VIP
还剩15页未读,
定制HR最喜欢的简历
你可能喜欢Qt信号与槽机制源码分析
在前一篇中,我翻译了一篇关于信号与槽机制详解的文章。在这一篇文章中,我将根据自己的理解从Qt源码中分析该机制。建议在看本文章之前先去看看前面提到的译文或者直接看原文原文。本文只分析了大概的思路,很多细节并没有深究下去,如有错误,敬请大家指正。
以下所有代码的测试基于Qt5.4.0,MOC版本是67。
同样,我们还是使用进行讲解。
#ifndef COUNTER_H
#define COUNTER_H
#include &QObject&
#include &QDebug&
class Counter : public QObject
int value() const {return m_}
public slots:
void setValue(int value);
void valueChanged(int newValue);
Counter();
~Counter();
#endif // COUNTER_H
#include "counter.h"
Counter::Counter()
m_value = 0;
void Counter::setValue(int value)
if(value != m_value)
m_value = value;
emit valueChanged(value);
Counter::~Counter()
#include "counter.h"
int main()
Counter a,
QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
qDebug() && "First :b.value() is " && b.value();
a.setValue(12);
qDebug() && "Second:b.value() is " && b.value();
我们在Debug目录下找到MOC根据Counter.h产生的C++文件:moc_counter.cpp
#include "../../Test/counter.h"
#include &QtCore/qbytearray.h&
#include &QtCore/qmetatype.h&
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'counter.h' doesn't include &QObject&."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.4.0. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
QT_BEGIN_MOC_NAMESPACE
struct qt_meta_stringdata_Counter_t {
QByteArrayData data[6];
char stringdata[46];
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_Counter_t, stringdata) + ofs \
- idx * sizeof(QByteArrayData)) \
static const qt_meta_stringdata_Counter_t qt_meta_stringdata_Counter = {
QT_MOC_LITERAL(0, 0, 7),
QT_MOC_LITERAL(1, 8, 12),
QT_MOC_LITERAL(2, 21, 0),
QT_MOC_LITERAL(3, 22, 8),
QT_MOC_LITERAL(4, 31, 8),
QT_MOC_LITERAL(5, 40, 5)
"Counter\0valueChanged\0\0newValue\0setValue\0"
#undef QT_MOC_LITERAL
static const uint qt_meta_data_Counter[] = {
QMetaType::Void, QMetaType::Int,
QMetaType::Void, QMetaType::Int,
void Counter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
if (_c == QMetaObject::InvokeMetaMethod) {
Counter *_t = static_cast&Counter *&(_o);
switch (_id) {
case 0: _t-&valueChanged((*reinterpret_cast& int(*)&(_a[1]))); break;
case 1: _t-&setValue((*reinterpret_cast& int(*)&(_a[1]))); 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;
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() : &staticMetaO
void *Counter::qt_metacast(const char *_clname)
if (!_clname) return Q_NULLPTR;
if (!strcmp(_clname, qt_meta_stringdata_Counter.stringdata))
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)
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id & 2)
qt_static_metacall(this, _c, _id, _a);
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id & 2)
*reinterpret_cast&int*&(_a[0]) = -1;
void Counter::valueChanged(int _t1)
void *_a[] = { Q_NULLPTR, const_cast&void*&(reinterpret_cast&const void*&(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
QT_END_MOC_NAMESPACE
上述的运行结果如下:
main()函数
在main()中,我们首先创建两个Counter类型的对象a和b:Counter a,。
之后我们将对象a中信号valueChanged(int)与对象b中的槽函数setValue(int)绑定起来:QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));。
最后调用a对象的槽函数:a.setValue(12);,调用完该函数之后,对象b中的m_value的值由原来的0变为12。很明显,我们的信号与槽连接成功。
setValue()函数
void Counter::setValue(int value)
if(value != m_value)
m_value = value;
emit valueChanged(value);
在该函数中,首先判断传入的参数是不是与Counter类的属性变量m_value是否不等,之所以做这一步,是因为如果两者相等还是emit信号,那么这个信号与槽的连接将进入死循环,永远无法结束。最后emit一个信号valueChanged(value)。在qobject.h中定义了emit这个宏:
qobjectdefs.h
# define emit // nothing
也就是说,在setValue()函数中只是纯粹的调用valueChanged()函数。
信号函数:valueChanged()
我们只在Counter.h声明了一个信号函数:
void valueChanged(int newValue);
但是我们并没有去实现它,那么它是怎么实现的呢?这时候就能体现出MOC的强大之处了。我们前面有提到过:MOC通过Counter.h函数生成代码保存在moc_counter.cpp的文件中。我们在该C++文件中找到了信号函数valueChanged()的实现代码:
void Counter::valueChanged(int _t1)
void *_a[] = { Q_NULLPTR, const_cast&void*&(reinterpret_cast&const void*&(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
reinterpret_cast运算符是用来处理无关类型之间的转换,它会产生一个新的值,这个值会与原始参数有完全相同的比特位。reinterpret_cast&const void*&(&_t1)的意思是将指向整型_t1的指针转换为const void*。
const_cast是用来溢出变量的const或volatile限定符。const_cast&void*&(reinterpret_cast&const void*&(&_t1))按我的理解是将指向整型_t1的指针转换为void*。我们只要记住,在这里_a[1]的值是一个指向信号函数参数的指针。
之后就调用QMetaObject::activate()并传入参数。
activate()参数:staticMetaObject
还是在moc_counter.cpp文件中,我们找到了staticMetaObject对象的初始化数据,它是QMetaObject类型。staticMetaObject包含了Counter类中一些非常重要的信息,需要多加注意。
const QMetaObject Counter = {
{ &QObject, qt_meta_stringdata_Counter.data,
qt_meta_data_Counter,
qt_static_metacall, Q_NULLPTR, Q_NULLPTR}
const QMetaObject *Counter() const
return QObject-&metaObject ? QObject-&dynamicMetaObject() : &staticMetaO
我们在qobjectdefs.h中找到了QMetaObject的构造函数
const QMetaObject *
const QByteArrayData *
const uint *
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_
const QMetaObject * const *relatedMetaO
因此我们可以得到如下的等式关系:
1、superdata = &QObject::staticMetaO
2、stringdata = qt_meta_stringdata_Counter.
3、data = qt_meta_data_C
4、static_metacall = qt_static_
5、relatedMetaObjects = Q_NULLPTR;
6、extradata = Q_NULLPTR;
其中2、3、4点的信息我们将会在后面用到。
QMetaObject::activate()函数
接下来我们来看信号函数valueChanged()中最后调用的QMetaObject::activate(this, &staticMetaObject, 0, _a);的函数原型。
QMetaObject::activate()定义在qobject.cpp文件中。
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)
activate()函数接收4个参数:
第1个参数表示发送信号的对象,这里当然是指对象a了;
第2个参数是指向QMetaObject类型的指针,这里我们传进去的是&staticMetaObject,该结构体中包含了很多重要的信息,我们在前面已经讲过了。
第3个参数是在这个类中该信号的索引值。因为我们在Counter类中只定义了一个信号ValueChanged(),所以该信号的索引值为0。那么如果我们定义多个信号,信号的索引值与信号定义出现的顺序呈一一对应关系。
第4个参数是接收一个指向指针的指针,这里传递的是指向数组的指针_a,我们前面也说了_a[1]的值是指向传递给信号函数valueChanged()的参数newValue,很明显,传递给newValue的值是12。。
QMetaObject::activate()的重载函数:void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)与前一个版本只差别在第2个参数,这里第2个参数传递的是信号的偏移量QMetaObjectPrivate::signalOffset(m)而不是指向QMetaObject对象的指针m。我一直没有找到QMetaObjectPrivate::signalOffset()的相关定义,在此根据其函数名理解该函数的作用是:从类中获取信号的偏移量。也就是在qt_meta_data_Counter数组中找到信号的偏移量。无论是调用哪个版本的activate()函数,都要告知其(传递参数给它)以下信息:
1、发送的对象sender;
2、该信号在所有信号中的偏移量signalOffset;
每一个对象中的每一个信号都维护了一个连接的双向列表connectionList(后面会提到),该链表中都有与该信号相互连接的信息,比如说接收对象,接收的槽函数,参数等等信息。只要找到该信号的偏移量,就可以找到其维护的双向列表,也就可以找到槽函数了。
3、该信号在该类中的索引值local_signal_index;
4、该信号携带的参数_a[1]。
我们这里讲了emit一个信号的过程,但是只提到了信号与槽机制的一点点皮毛。为什么emit一个信号后会有一个槽函数响应呢?信号与槽是怎样绑定的?都绑定哪些信息呢?
QObject::connect()
在main()函数中,我们调用了QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));将信号与槽绑定起来。在分析connect函数之前,我们先来看看宏SIGNAL()和SLOT()都是什么鬼。
宏SIGNAL和SLOT
我们在qobjectdefs.h中找到了那两个宏的定义:
#ifndef QT_NO_META_MACROS
#ifndef QT_NO_DEBUG
# define QLOCATION "\0" __FILE__ ":" QT_STRINGIFY(__LINE__)
# ifndef QT_NO_KEYWORDS
define METHOD(a)
qFlagLocation("0"#a QLOCATION)
# define SLOT(a)
qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a)
qFlagLocation("2"#a QLOCATION)
# ifndef QT_NO_KEYWORDS
define METHOD(a)
# define SLOT(a)
# define SIGNAL(a)
这两个宏无非是将信号与槽函数的名称(函数名以及参数)转换为字符串,仅此而已。在方法名称前面给个标识0,在槽函数前面给个标识1,在信号函数前面给个标识2,这3个标识为后面解析参数是所属哪种类型的起到很大的作用。我们在这边是不是可以推测这就是很多编程语言的函数名不能以数字开头的原因之一吗?还有我们注意到,这里的宏是将信号与槽的名称转换为字符串,这里的名称包括了参数。于是我们可以得出:信号与槽的参数不能包括宏。如果包括宏,是要等里面的宏先展开然后SLOT和SIGNAL再展开还是先SLOT和SIGNAL展开呢?这都是没有定义的。
Qt4.8版本以前的connect()
好激动,大boss出现了,秒了它。。。
我们在这里使用的是Qt4.8版本之前旧的connect()语法,如果想要理解Qt5.0以上版本该语法的解释,可以去看这篇文章:.
我们首先看看这个函数介绍(在qobject.cpp文件中):
threadsafe
Creates a connection of the given \a type from the \a signal in
the \a sender object to the \a method in the \a receiver object.
Returns a handle to the connection that can be used to disconnect
You must use the \c SIGNAL() and \c SLOT() macros when specifying
the \a signal and the \a method, for example:
\snippet code/src_corelib_kernel_qobject.cpp 22
This example ensures that the label always displays the current
scroll bar value. Note that the signal and slots parameters must not
contain any variable names, only the type. E.g. the following would
not work and return false:
\snippet code/src_corelib_kernel_qobject.cpp 23
A signal can also be connected to another signal:
\snippet code/src_corelib_kernel_qobject.cpp 24
In this example, the \c MyWidget constructor relays a signal from
a private member variable, and makes it available under a name
that relates to \c MyWidget.
A signal can be connected to many slots and signals. Many signals
can be connected to one slot.
If a signal is connected to several slots, the slots are activated
in the same order in which the connections were made, when the
signal is emitted.
The function returns a QMetaObject::Connection that represents
a handle to a connection if it successfully
connects the signal to the slot. The connection handle will be invalid
if it cannot create the connection, for example, if QObject is unable
to verify the existence of either \a signal or \a method, or if their
signatures aren't compatible.
You can check if the handle is valid by casting it to a bool.
By default, a signal is emitted for every
two signals are emitted for duplicate connections. You can break
all of these connections with a single disconnect() call.
If you pass the Qt::UniqueConnection \a type, the connection will only
be made if it is not a duplicate. If there is already a duplicate
(exact same signal to the exact same slot on the same objects),
the connection will fail and connect will return an invalid QMetaObject::Connection.
The optional \a type parameter describes the type of connection
to establish. In particular, it determines whether a particular
signal is delivered to a slot immediately or queued for delivery
at a later time. If the signal is queued, the parameters must be
of types that are known to Qt's meta-object system, because Qt
needs to copy the arguments to store them in an event behind the
scenes. If you try to use a queued connection and get the error
\snippet code/src_corelib_kernel_qobject.cpp 25
call qRegisterMetaType() to register the data type before you
establish the connection.
\sa disconnect(), sender(), qRegisterMetaType(), Q_DECLARE_METATYPE()
上面的介绍总结起来有以下几点:
1、返回值是一个该连接的句柄,用于删除连接与表示连接成功与否;
2、这个版本的connect()必须要使用SIGNAL() 和 SLOT()宏指定信号和槽;
3、一个信号可以与其他信号连接;
4、一个信号可以与许多槽函数或者信号连接;多个信号可以与一个槽函数连接;
5、当一个信号与多个槽函数连接时,槽函数的响应是按照connect的顺序来的;
6、可选的参数用来指定槽函数是立即响应信号或者是稍后响应。
接下来我们要看到connect()的真面目了,我们在main.c里面调用了
QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
我们把宏SIGNAL()和SLOT()展开,实际上传递的参数是如下的:
QObject::connect(&a, "2valueChanged(int)", &b, "1setValue(int)")
最后,我们来看看connect()的函数实现吧:
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
sender ? sender-&metaObject()-&className() : "(null)",
(signal && *signal) ? signal+1 : "(null)",
receiver ? receiver-&metaObject()-&className() : "(null)",
(method && *method) ? method+1 : "(null)");
return QMetaObject::Connection(0);
QByteArray tmp_signal_
if (!check_signal_macro(sender, signal, "connect", "bind"))
return QMetaObject::Connection(0);
const QMetaObject *smeta = sender-&metaObject();
const char *signal_arg =
++ //skip code
QArgumentTypeArray signalT
Q_ASSERT(QMetaObjectPrivate::get(smeta)-&revision &= 7);
QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); // (4)
int signal_index = QMetaObjectPrivate::indexOfSignalRelative(
&smeta, signalName, signalTypes.size(), signalTypes.constData());
if (signal_index & 0) {
// check for normalized signatures
tmp_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());
if (signal_index & 0) {
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return QMetaObject::Connection(0);
signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
signal_index += QMetaObjectPrivate::signalOffset(smeta);
QByteArray tmp_method_
int membcode = extract_code(method);
if (!check_method_code(membcode, receiver, method, "connect"))
return QMetaObject::Connection(0);
const char *method_arg =
++ // skip code
QByteArray methodN
QArgumentTypeArray methodT
const QMetaObject *rmeta = receiver-&metaObject();
int method_index_relative = -1;
Q_ASSERT(QMetaObjectPrivate::get(rmeta)-&revision &= 7);
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
if (method_index_relative & 0) {
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
methodTypes.clear();
methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
// rmeta may have been modified above
rmeta = receiver-&metaObject();
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
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"
%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);
QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
QMetaMethod rmethod = rmeta-&method(method_index_relative + rmeta-&methodOffset());
check_and_warn_compat(smeta, smethod, rmeta, rmethod);
QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));
我来分别解释一下代码中各段的意思:
(1):参数的有效性检查
判断传递进来的4个参数指针是否都为NULL,若有NULL,提供警告信息并返回错误;
(2):检查信号的有效性
实际上就是判断SIGNAL()展开后的得到的字符串的前面的标识是否为2。
if (!check_signal_macro(sender, signal, "connect", "bind"))
return QMetaObject::Connection(0);
static bool check_signal_macro(const QObject *sender, const char *signal,
const char *func, const char *op)
int sigcode = extract_code(signal);
if (sigcode != QSIGNAL_CODE) {
if (sigcode == QSLOT_CODE)
qWarning("QObject::%s: Attempt to %s non-signal %s::%s",
func, op, sender-&metaObject()-&className(), signal+1);
qWarning("QObject::%s: Use the SIGNAL macro to %s %s::%s",
func, op, sender-&metaObject()-&className(), signal);
return false;
return true;
(3):获取得到staticMetaObject,实际上就是Counter类的详细信息moc_counter.cpp,我们在文章的开头处有讲过了,这里面包含了很多该重要信息。
const QMetaObject *smeta = sender-&metaObject();
(4):将信号的名称与参数分开
QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
QByteArray QMetaObjectPrivate::decodeMethodSignature(
const char *signature, QArgumentTypeArray &types)
Q_ASSERT(signature != 0);
const char *lparens = strchr(signature, '(');
if (!lparens)
return QByteArray();
const char *rparens = strrchr(lparens + 1, ')');
if (!rparens || *(rparens+1))
return QByteArray();
int nameLength = lparens -
argumentTypesFromString(lparens + 1, rparens, types);
return QByteArray::fromRawData(signature, nameLength);
将信号的名称作为返回值给signalName,将参数信息传递给signalTypes。把信号的名称与参数分开。
(5):第(5)到第(6)之间的内容看不懂,只知道它是为了获取信号在类中的索引值,为什么要大费周章的获取真是搞不明白。
推测与moc_counter.cpp中的qt_meta_data_Counter有关。
(6):从(6)开始往下到(8)是处理槽函数相关的工作,它所做的处理与信号函数是一样的。
(7):从(7)的代码中看到switch case 语句不仅检测SLOT的标识符1并且还检测SIGNAL的标识符2,是不是信号也可以和信号绑定的呢??我有在代码中进行验证,将信号与信号connect在一起,我在main.c中稍微做了修改:
int main()
Counter a, b,
QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SIGNAL(valueChanged(int)));
QObject::connect(&b, SIGNAL(valueChanged(int)), &c, SLOT(setValue(int)));
qDebug() && "First :c.value() is " && c.value();
a.setValue(12);
qDebug() && "Second:c.value() is " && c.value();
很明显,c.value()的结果是12。所以说:信号与信号是可以绑定的。
(8):检测信号函数和槽函数的参数类型以及参数个数是否是一致的。
(9):判断connect()第五个可选参数的连接类型。
(10):好吧,这个大boss还有分身。
QMetaObject handle = QMetaObject(QMetaObjectPrivate(
sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));
好,让我们看看这个分身的原型:
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)
它的返回值是一个指向QObjectPrivate::Connection类型的指针。
第1个参数是发送信号的对象sender;
第2个参数是信号在类中的索引值signal_index;
第3个参数是发送对象的元对象smeta;
第4个参数是响应信号的对象receiver;
第5个参数是响应信号槽函数的索引值method_index_relative;
第6个参数是接收对象的元对象rmeta;
第7个和第8个参数是该连接的类型。
那我们继续看该分身的实现,有些内容将省略。
QObjectPrivate *QMetaObjectPrivate(const QObject *sender,
int signal_index, const QMetaObject *smeta,
const QObject *receiver, int method_index,
const QMetaObject *rmeta, int type, int *types)
QObjectPrivate callFunction =
rmeta ? rmeta-&d.static_metacall : 0;
QScopedPointer&QObjectPrivate& c(new QObjectPrivate);
c-&sender =
c-&signal_index = signal_
c-&receiver =
c-&method_relative = method_
c-&method_offset = method_
c-&connectionType = type;
c-&isSlotObject = false;
c-&argumentTypes.store(types);
c-&nextConnectionList = 0;
c-&callFunction = callF
QObjectPrivate(s)-&addConnection(signal_index, c.data());
locker.unlock();
QMetaMethod smethod = QMetaObjectPrivate(smeta, signal_index);
if (smethod.isValid())
s-&connectNotify(smethod);
return c.take();
QObjectPrivate::StaticMetaCallFunction callFunction =
rmeta ? rmeta-&d.static_metacall : 0
这个语句是从接收对象的元对象中获取调用函数(回调函数)的信息static_metacall。我们在前面得出
static_metacall = qt_static_
qt_static_metacall又在moc_counter.cpp文件中,所以说moc_counter.cpp很重要吧。
接下来是创建一个QObjectPrivate::Connection类型的变量c(这个好像是模板吧,对于我的C++知识表示很着急。。)。反正我只知道把一些相关信息保存在connectionList中。至于怎样保存?如何保存?保存哪些东西?现在都还没有深究过。后面再把这里的知识补上。
1、当我们在xxx.h文件中声明了信号和槽函数相关的内容,MOC会根据该内容生成一个对相应moc_xxx.cpp的文件,里面包含了该.h文件中很多重要的信息,比如信号与槽的索引值;
2、当我们在程序中调用connect()函数绑定信号与槽,实际上在内部做了很多个工作。为发送对象的信号所维护的connectionLists中添加接收对象的相关信息(名称、槽函数名称、参数类型……),实际上就是信息的绑定。
3、当我们在程序中emit一个信号,会从该信号维护的connectionLists找到与之绑定的槽函数并执行。
4、Qt中信号与槽的机制确实是通过回调函数实现的,但并不仅仅是这样的。它在内部做了很多的努力,比如安全性的保证、比如使用connectionLists保证信号与槽简单方便的使用。
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!}

我要回帖

更多关于 信号与槽机制 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信