收藏 分享(赏)

Inside_Qt_系列.pdf

上传人:精品资料 文档编号:11126561 上传时间:2020-02-08 格式:PDF 页数:24 大小:521.72KB
下载 相关 举报
Inside_Qt_系列.pdf_第1页
第1页 / 共24页
Inside_Qt_系列.pdf_第2页
第2页 / 共24页
Inside_Qt_系列.pdf_第3页
第3页 / 共24页
Inside_Qt_系列.pdf_第4页
第4页 / 共24页
Inside_Qt_系列.pdf_第5页
第5页 / 共24页
点击查看更多>>
资源描述

1、Inside Qt 系列 QObject 这个 class 是 QT 对象模型的核心,绝大部分的 QT 类都是从这个类继承而来。这个模型的中心特征就是一个叫做信号和槽( signal and slot)的机制来实现对象间的通讯,你可以把一个信号和另一个槽通过 connect() 方法连接起来,并可以使用 disconnect() 方法来断开这种连接,你还可以通过调用 blockSignal() 这个方法来临时的阻塞信号,QObject 把它们自己组织在对象树中。当你创建一个 QObject 并使用其它对象作为父对象时,这个对象会自动添加到父对象的 children() list 中。父对象拥有

2、这个对象,比如,它将在它的析构函数中自动删除它所有的 child 对象。你可以通过 findChild() 或者 findChildren()函数来查找一个对象。每个对象都有一个对象名称( objectName())和类名称( class name) , 他们都可以通过相应的 metaObject 对象来获得。你还可以通过 inherits() 方法来判断一个对象的类是不是从另一个类继承而来。当对象被删除时,它发出destroyed()信号。你可以捕获这个信号来避免对 QObject的无效引用。QObject可以通过 event()接收事件并且过滤其它对象的事件。详细情况请参考 install

3、EventFilter()和 eventFilter()。对于每一个实现了信号、槽和属性的对象来说, Q_OBJECT 宏都是必须要加上的。 QObject 实现了这么多功能,那么,它是如何做到的呢?让我们通过它的 Source Code 来解开这个秘密吧。 QObject 类的实现文件一共有四个 : * qobject.h, QObject class 的基本定义,也是我们一般定义一个类的头文件 * qobject.cpp, QObject class 的实现代码基本上都在这个文件 * qobjectdefs.h,这个文件中最重要的东西就是定义了 QMetaObject class,这个 c

4、lass是为了实现 signal、 slot、 properties,的核心部分。 * qobject_p.h,这个文件中的 code 是辅助实现 QObject class 的,这里面最重要的东西是定义了一个 QObjectPrivate 类来存储 QOjbect 对象的 成员数据。 理解这个 QObjectPrivate class 又是我们理解 QT kernel source code 的基础,这个对象包含了每一个 QT 对象中的数据成员,好了,让我们首先从理解 QObject 的数据存储代码开始我么的 QT Kernel Source Code 之旅。 我们知道,在 C+中,几乎每一

5、个类 (class)中都需要有一些类的成员变量(class member variable),在通常情况下的做法如下: class Person private: string mszName; / 姓名 bool mbSex; / 性别 int mnAge; / 年龄 ; 就是在类定义的时候,直接把类成员变量定义在这里,甚至于,把这些成员变量的存取范围直接定义成是 public 的,您是不是这是这样做的呢? 在 QT 中,却几乎都不是这样做的,那么, QT是怎么做的呢? 几乎每一个 C+的类中都会保存许多的数据,要想读懂别人写的 C+代码,就一定需要知道每一个类的的数据是如何存储的,是什么含

6、义,否则,我们不可能读懂别人的 C+代码。在这里也就是说,要想读懂 QT的代码,第一 步就必须先搞清楚 QT的类成员数据是如何保存的。 为了更容易理解 QT是如何定义类成员变量的,我们先说一下 QT 2.x 版本中的类成员变量定义方法,因为在 2.x 中的方法非常容易理解。然后在介绍 QT 4.4 中的类成员变量定义方法。 QT 2.x 中的方法 在定义 class的时候 (在 .h文件中 ),只包含有一个类成员变量,只是定义一个成员数据指针,然后由这个指针指向一个数据成员对象,这个数据成员对象包 含所有这个 class的成员数据,然后在 class的实现文件 (.cpp文件 )中,定义这个私

7、有数据成员对象。示例 代码如下: /- / File name: person.h struct PersonalDataPrivate; / 声明私有数据成员类型 class Person public: Person (); / constructor virtual Person (); / destructor void setAge(const int); int getAge(); private: PersonalDataPrivate* d; ; /- / File name: person.cpp struct PersonalDataPrivate / 定义私有数据成员类型

8、 string mszName; / 姓名 bool mbSex; / 性别 int mnAge; / 年龄 ; / constructor Person:Person () d = new PersonalDataPrivate; ; / destructor Person:Person () delete d; ; void Person:setAge(const int age) if (age != d-mnAge) d-mnAge = age; int Person:getAge() return d-mnAge; 在最初学 习 QT的时候,我也觉得这种方法很麻烦,但是随着使用的增多

9、,我开始很喜欢这个方法了,而且,现在我写的代码,基本上都会用这种方法。具体说来,它有如下优点: * 减少头文件的依赖性 把具体的数据成员都放到 cpp文件中去,这样,在需要修改数据成员的时候,只需要改 cpp文件而不需要头文件,这样就可以避免一次因为头文件的修改而导 致所有包含了这个文件的文件全部重新编译一次,尤其是当这个头文件是非常底层的头文件和项目非常庞大的时候,优势明显。 同时,也减少了这个头文件对其它头文件的依赖性。可以把只在数据成员中需要用到的在 cpp文件中 include一次就可以,在头文件中就可以尽可能的减少include语句 * 增强类的封装性 这种方法增强了类的封装性,无法

10、再直接存取类成员变量,而必须写相应的 get/set 成员函数来做这些事情。 关于这个问题,仁者见仁,智者见智,每个人都有不同的观点。有些人就是喜欢把类成员变量都定义成 public的,在使用的时候方便。只是我个人不喜欢这 种方法,当项目变得很大的时候,有非常多的人一起在做这个项目的时候,自己所写的代码处于底层有非常多的人需要使用 (#include)的时候,这个方法 的弊端就 充分的体现出来了。 还有,我不喜欢 QT 2.x 中把数据成员的变量名都定义成只有一个字母, d,看起来很不直观,尤其是在 search的时候,很不方便。但是, QT kernel 中的确就是这么干的。 QT 4.4.

11、x 中的方法 在 QT 4.4 中,类成员变量定义方法的出发点没有变化,只是在具体的实现手段上发生了非常大的变化,下面具体来看。 在 QT 4.4 中,使用了非常多的宏来做事,这凭空的增加了理解 QT source code 的难度,不知道他们是不是从 MFC学来的。就连在定义类成员数据变量这件事情上 ,也大量的使用了宏。 在这个版本中,类成员变量不再是给每一个 class都定义一个私有的成员,而是把这一项 common的工作放到了最基础的基类 QObject 中,然后定义了一些相关的方法来存取,好了,让我们进入具体的代码吧。 /- / file name: qobject.h class Q

12、ObjectData public: virtual QObjectData() = 0; / 省 略 ; class QObject Q_DECLARE_PRIVATE(QObject) public: QObject(QObject *parent=0); protected: QObject(QObjectPrivate dd, QObject *parent = 0); QObjectData *d_ptr; 这些代码就是在 qobject.h 这个头文件中的。在 QObject class 的定义中,我们看到,数据员的定义为: QObjectData *d_ptr; 定义成 prot

13、ected 类型的就是要让所有的派生类都可以存取这个变量,而在外部却不可以直接存取这个变量。而 QObjectData 的定义却放在了这个头文件中,其目的就是为了要所有从 QObject继承出来的类的成员变量也都相应的要在 QObjectData这个 class继承出 来。而纯虚的析构函数又决定了两件事: * 这个 class不能直接被实例化。换句话说就是,如果你写了这么一行代码,new QObjectData, 这行代码一定会出错, compile的时候是无法过关的。 * 当 delete 这个指针变量的时候,这个指针变量是指向的任意从QObjectData继承出来的对象的时候,这个对象都能

14、被正确 delete,而不会产生错误,诸如,内存泄漏之类的。 我们再来看看这个宏做了什么, Q_DECLARE_PRIVATE(QObject) #define Q_DECLARE_PRIVATE(Class) inline Class#Private* d_func() return reinterpret_castClass#Private *(d_ptr); inline const Class#Private* d_func() const return reinterpret_castconst Class#Private *(d_ptr); friend class Class#P

15、rivate; 这个宏主要是定义了两个重载的函数, d_func(),作用就是把在 QObject这个 class中定义的数据成员变量 d_ptr安全的转换成为每一个具 体的 class的数据成员类型指针。我们看一下在 QObject这个 class中,这个宏展开之后 的情况,就一幕了然了。 Q_DECLARE_PRIVATE(QObject) 展开后,就是下面的代码: inline QObjectPrivate* d_func() return reinterpret_cast(d_ptr); inline const QObjectPrivate* d_func() const retur

16、n reinterpret_cast;(d_ptr); friend class QObjectPrivate; 宏展开之后,新的问题又来了,这个 QObjectPrivate是从哪里来的?在QObject这个 class中,为什么不直接使用 QObjectData来数据成员变量的类型? 还记得我们刚才说过吗, QObjectData这个 class的析构函数的纯虚函数,这就说明这个 class是不能实例化的,所以, QObject这个 class的成员变量的实际类型,这是从 QObjectData继承出来的,它就是 QObjectPrivate ! 这个 class 中保存了许多非常重要而且

17、有趣的东西 ,其中包括 QT 最核心的 signal 和 slot 的数据,属性数据,等等,我们将会在后面详细讲解,现在我们来看一下它的定义: 下面就是这个 class的定义: class QObjectPrivate : public QObjectData Q_DECLARE_PUBLIC(QObject) public: QObjectPrivate(int version = QObjectPrivateVersion); virtual QObjectPrivate(); / 省略 那么,这 个 QObjectPrivate 和 QObject 是什么关系呢?他们是如何关联在一起的呢

18、? 接上节,让我们来看看这个 QObjectPrivate 和 QObject 是如何关联在一起的。 /- / file name: qobject.cpp QObject:QObject(QObject *parent) : d_ptr(new QObjectPrivate) / QObject:QObject(QObjectPrivate dd, QObject *parent) : d_ptr(dd) / 怎么样,是不是一目了然呀? 从第一个构造函数可以很清楚的看出来, QObject class 中的 d_ptr 指针将指向一个 QObjectPrivate 的对象,而 QObject

19、Private这个 class是从 QObjectData继承出来的。 这第二个构造函数干什么用的呢?从 QObject class 的定义中,我们可以看到,这第二个构造函数是被定义为 protected 类型的,这说明,这个构造函数只能被继承的 class使用,而不能使用这个构造函数来直接构造一个QObject对象,也就是说,如果写一条下面的语句, 编译的时候是会失败的, new QObject(*new QObjectPrivate, NULL) 为了看的更清楚,我们以 QWidget这个 class为例说明。 QWidget是 QT中所有 UI控件的基类,它直接从 QObject继承而来

20、, class QWidget : public QObject, public QPaintDevice Q_OBJECT Q_DECLARE_PRIVATE(QWidget) / . 我们看一个这个 class的构造函数的代码: QWidget:QWidget(QWidget *parent, Qt:WindowFlags f) : QObject(*new QWidgetPrivate, 0), QPaintDevice() d_func()-init(parent, f); 非常清楚,它调用了基类 QObject的保护类型的构造函数,并且以 *new QWidgetPrivate 作为

21、第一个参数传递进去。也就是说,基类 (QObject)中的 d_ptr指针将会指向一个 QWidgetPrivate类型的对象。 再看 QWidgetPrivate这个 class的定义: class QWidgetPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QWidget) / . 好了,这就把所有的事情都串联起来了。 关于 QWidget构造函数中的唯一的语句 d_func()-init(parent, f) 我们注意到在 class的定义中有这么一句话: Q_DECLARE_PRIVATE(QWidget) 我们前面讲过这个宏,当把这

22、个宏展开之后,就是这样的: inline QWidgetPrivate* d_func() return reinterpret_castQWidgetPrivate *(d_ptr); inline const QWidgetPrivate* d_func() const return reinterpret_castconst QWidgetPrivate *(d_ptr); friend class QWidgetPrivate; 很清楚,它就是把 QObject中定义的 d_ptr 指针转换为 QWidgetPrivate类型的指针。 小结: 要理解 QT Kernel的 code,就

23、必须要知道 QT中每一个 Object内部的数据是如何保存的,而 QT没有象我们平时写 code一样,把所有的变量直接定义在类 中,所以,不搞清楚这个问题,我们就无法理解一个相应的 class。其实,在 QT4.4中的类成员数 据的保存方法在本质是与 QT2.x中的是一样的,就是 在class中定义一个成员数据的指针,指向成员数据集合对象 (这里是一个QObjectData或者是其派生类 )。初始化这个成员变量的办法是定义一个 保护类型的构造函数,然后在派生类的构造函数 new 一个派生类的数据成员,并将这个新对象赋值给 QObject的数据指针。在使用的时候,通过预先定义个宏里面的一个 in

24、line函数来把数据指针在安全类 型转换,就可以使用了。 从本节开始,我们讲解 QT Meta-Object System 的功能,以及实现。 在使用 Qt 开发的过程中,大量的使用了 signal 和 slot. 比如,响应一个 button 的 click 事件,我们一般都写如下的代码: class MyWindow : public QWidget Q_OBJECT public: MyWindow(QWidget* parent) : QWidget(parent) QPushButton* btnStart = new QPushButton(“start”, this); conn

25、ect(btnStart, SIGNAL(clicked(), SLOT(slotStartClicked(); private slots: void slotStartClicked(); ; void MyWindow: slotStartClicked() / 省略 在这段代码中,我们把 btnStart 这个 button 的 clicked() 信号和 MyWindow 的 slotStartClicked() 这个槽相连接,当 btnStart 这个 button 被用户按下 (click)的时候,就会发出一个 clicked() 的信号,然后, MyWindow: slotSt

26、artClicked() 这个 slot 函数就会被调用用来响应 button 的 click 事件。 这段代码是最为典型的 signal/slot 的应用实例,在实际的工作过程中,signal/slot 还有更为广泛的应用。准确的说, signal/slot 是 QT提供的一种在对象间进行通讯的技术,那么,这个技术在 QT 中是如何实现的呢? 这就是 QT 中的元对象系统 (Meta Object System)的作用,为了更好的理解它,让我先来对它的功 能做一个回顾,让我们一起来揭开它神秘的面纱。 Meta-Object System 的基本功能 Meta Object System 的设

27、计基于以下几个基础设施: * QObject 类 作为每一个需要利用元对象系统的类的基类 * Q_OBJECT 宏, 定义在每一个类的私有数据段,用来启用元对象功能,比如,动态属性,信号和槽 * 元对象编译器 moc (the Meta Object Complier), moc 分析 C源文件,如果它发现在一个头文件 (header file)中包含Q_OBJECT 宏定 义,然后动态的生成另外一个 C+源文件,这个新的源文件包含 Q_OBJECT 的实现代码,这个新的 C+ 源文件也会被编译、链接到这个类的二进制代码中去,因为它也是这个类的完整的一部分。通常,这个新的 C+ 源文件会在以前

28、的 C+ 源文件名前面加上 moc_ 作为新文件的文件名。其具体过程如下图所示: 除了提供在对象间进行通讯的机制外,元对象系统还包含以下几种功能: * QObject:metaObject() 方法 它获得与一个类相关联的 meta-object * QMetaObject:className() 方法 在运行期间返回一个对象的类名,它不需要本地 C+编译器的 RTTI(run-time type information)支持 * QObject:inherits() 方法 它用来判断生成一个对象类是不是从一个特定的类继承出来,当然,这必须是在QObject类的直接或者间接派生类当中 * QO

29、bject:tr() and QObject:trUtf8() 这两个方法为软件的国际化翻译字符串 * QObject:setProperty() and QObject:property() 这两个方法根据属性名动态的设置和获取属性值 除了以上这些功能外,它还使用 qobject_cast()方法在 QObject类之间提供动态转换, qobject_cast()方法的功能类似于标准 C+的dynamic_cast(),但是 qobject_cast()不需要 RTTI的支持,在一个QObject类或者它的派生类中,我们可以不定 义 Q_OBJECT宏。如果我们在一个类中没有定义 Q_OBJ

30、ECT宏,那么在这里所提到的相应的功能在这个类中也不能使用,从 meta-object的 观点来说,一个没有定 义 Q_OBJECT宏的类与它最接近的那个祖先类是相同的,那就是所, QMetaObject:className() 方法所返回的名字并不是这个类的名字,而是与它最接近的那个祖先类的名字。所以,我们强烈建议,任何从 QObject继承出来的类都定义 Q_OBJECT 宏。 下一节,我们来了解另一个重要的工具: Meta-Object Compiler 元对象编译器用来处理 QT 的 C+扩展, moc 分析 C源文件,如果它发现在一个头文件 (header file)中包含 Q_OB

31、JECT 宏定义,然后动态的生成另外一 个 C+源文件,这个新的源文件包含 Q_OBJECT 的实现代码,这个新的 C+ 源文件也会被编译、链接到这个类的二进制代码中去,因为它也是这个类的完整的一部分。通常,这个新的 C+ 源文件会在以前的 C+ 源文件名前面加上 moc_ 作为新文件的文件名。 如果使用 qmake工具来生成 Makefile文件,所有需要使用 moc的编译规则都会给自动的包含到 Makefile文件中,所以对程序员来说不需要直接的使用 moc 除了处理信号和槽之外, moc还处理属性信息, Q_PROPERTY()宏定义类的属性信息,而 Q_ENUMS()宏则定义在一个类中

32、的枚举类型列表。 Q_FLAGS()宏定义在一个类中的 flag枚举类型列表, Q_CLASSINFO()宏则允许你在一个类的 meta信息中插入 name/value 对。 由 moc所生成的文件必须被编译和链接,就象你自己写的另外一个 C+文件一样,否则,在链接的过程中就会失败。 Code example: class MyClass : public QObject Q_OBJECT Q_PROPERTY(Priority priority READ priority WRITE setPriority) Q_ENUMS(Priority) Q_CLASSINFO(“Author“, “

33、Oscar Peterson“) Q_CLASSINFO(“Status“, “Active“) public: enum Priority High, Low, VeryHigh, VeryLow ; MyClass(QObject *parent = 0); virtual MyClass(); void setPriority(Priority priority); Priority priority() const; ; 本节介绍 Signal和 slot的基本知识。 信号和 槽是用来在对象间通讯的方法,当一个特定事件发生的时候, signal会被 emit 出来, slot 调用是用

34、来响应相应的 signal 的。 QT 对象已经包含了许多预定义的 signal,但我们总是可以在派生类中添加新的 signal。QT 对象中也已经包含了许多预定义的 slot,但我们可以在派生类中添加新的 slot 来处理我们感兴趣的 signal. signal 和 slot 机制是类型安全的, signal 和 slot必须互相匹配 (实际上,一个 solt的参数可以比对应的 signal 的参数少,因为它可以忽略多余的参数 )。 signal 和 slot是松散的配对关系,发出 signal的对象不关心是那个对象链接了 这个 signal,也不关心是那个或者有多少 slot链接到了这个

35、 signal。 QT的 signal 和 slot机制保证了,如果一个 signal和 slot相链接, slot会在正确的时机被调用,并且是使用正确的参数。 Signal和 slot都可以携带任 何数量和类型的参数,他们都是类型安全的。 所有从 QObject直接或者间接继承出来的类都能包含信号和槽 ,当一个对象的状态发生变化的时候,信号就可以被 emit出来,这可能是某个其它的对象所 关心的。这个对象并不关心有那个对象或者多少个对象链接到这个信号了,这是真实的信息封装,它保证了这个对象可以作为一个软件组件来被使用。 槽 (slot)是用来接收信号的,但同时他们也是一个普通的类成员函数,就

36、象一个对象不关心有多少个槽链接到了它的某个信号,一个对象也不关心一个槽链接了多少个信号。这保证了用 QT创建的对象是一个真实的独立的软件组件。 一个信号可以链接到多个槽,一个槽也可以链接多个信号。同时,一个信号也可以链接到另 外一个信号。所有使用了信号和槽的类都必须包含 Q_OBJECT 宏,而且这个类必须从 QObject类派生 (直接或者间接派生 )出来, 当一个 signal被 emit出来的时候,链接到这个 signal的 slot会立刻被调用,就好像是一个函数调用一样。当这件事情发生的时 候, signal和 slot机制与 GUI的事件循环完全没有关系,当所有链接到这个 signa

37、l的 slot执行完成之后,在 emit 代码行之后的代码会立刻被执行。当有多个 slot链接到一个 signal的时候,这些 slot会一个接着一个的、 以随机的顺序被 执行 。 Signal 代码会由 moc 自动生成,开发人员一定不能在自己的 C+代码中实现它,并且,它永远都不能有返回值。 Slot其实就是一个普通的类函数,并且可以被直接调用,唯一特殊 的地方是它可以与 signal相链接。 C+的预处理器更改或者删除 signal, slot, emit 关键字,所以,对于 C+编译器来说,它处理的是标准的 C+源文件。 如下图所示:假定 QPushButton 的 signal cl

38、icked() 已经和 QLineEdit 的 slot clear() 连接成功,那 么当 QPushButton 的 clicked() signal 被 emit 出来的时候, QLineEdit 的 clear() slot 就会被调用。 前面我们介绍了 Meta Object 的基本功能,和它支持的最重要的特性之一:Signal virtual const QMetaObject *metaObject() const; virtual void *qt_metacast(const char *); QT_TR_FUNCTIONS virtual int qt_metacall(Q

39、MetaObject:Call, int, void *); private: 这里,我们先忽略 Q_OBJECT_CHECK 和 QT_TR_FUNCTIONS 这两个宏。 我们看到,首先定义了一个静态类型的类变量 staticMetaObject,然后有一个获取这个对象指针的方法 metaObject()。这里最重要的 就是类变量staticMetaObject 的定义。 这说明所有的 QObject 的对象都会共享这一个 staticMetaObject 类变量,靠它来完成所有信号和槽的功能,所以我们就有必要来仔细的看看它是怎么回事了。 我们来看一下 QMetaObject的定义,我们先

40、看一下 QMetaObject对象中包含的成员数据。 struct Q_CORE_EXPORT QMetaObject / struct / private data const QMetaObject *superdata; const char *stringdata; const uint *data; const void *extradata; d; ; 上面的代码就是 QMetaObject类所定义的全部数据成员。就是这些成员记录了所有 signal, slot, property, class information这么多的信息。下面让我们来逐一解释这些成员变量: const Q

41、MetaObject *superdata: 这个变量指向与之对应的 QObject类的父类,或者是祖先类的 QMetaObject对象。 如何理解这一句话呢?我们知道,每一个 QMetaObject对象,一定有一个与之相对应的 QObject类 (或者由其直接或间接派生出的子类 ),注意:这里是类,不是对象。 那么每一个 QObject类 (或其派生类 )可能有一个父类,或者父类的父类,或者很多的继承层次之前的祖先类。或者没有父类 (QObject)。那么 superdata 这个变量就是指向与其最接近的祖先类中的 QMetaObject对象。对于QObject类 QMetaObject对象

42、来说,这是一个 NULL指针,因为 QObject没有父类。 下面,让我们来举例说明: class Animal : public QObject Q_OBJECT /. ; class Cat : public Animal Q_OBJECT /. 那么, Cat:staticMetaObject.d.superdata 这个指针变量指向的对象是 Animal:staticMetaObject 而 Animal:staticMetaObject.d.superdata 这个指针变量指向的对象是 QObject:staticMetaObject. 而 QObject:staticMetaObj

43、ect.d.superdat 这个指针变量的值为 NULL。 但如果我们把上面 class的定义修改为下面的定义,就不一样了: class Animal : public QObject / Q_OBJECT,这个 class 不定义这个 /. ; class Cat : public Animal Q_OBJECT /. 那么, Cat:staticMetaObject.d.superdata 这个指针变量指向的对象是 QObject:staticMetaObject 因为 Animal:staticMetaObject 这个对象是不存在的。 const char *stringdata:

44、顾名思义,这是一个指向 string data的指针。但它和我们平时所使用的一般的字符串指针却很不一样,我们平时使用的字符串指针只是指向一个字符串的指针,而这个指针却指向 的是很多个字 符串。那么它不就是字符串数组吗?哈哈,也不是。因为 C+的字符串数组要求数组中的每一个字符串拥有相同的长度,这样才能组成一个数组。那它是不是一个 字符串指针数组呢?也不是,那它到底是什么呢?让我们来看一看它的具体值,还是让我们以 QObject这个 class的 QMetaObject为例来说明 吧。 下面是 QObject:staticMetaObject.d.stringdata指针所指向的多个字符串数组,

45、其实它就是指向一个连续的内存区,而这个连续的内存区中保存了若干个字符串。 static const char qt_meta_stringdata_QObject = “QObject00destroyed(QObject*)0destroyed()0“ “deleteLater()0_q_reregisterTimers(void*)0“ “QString0objectName0parent0QObject(QObject*)0“ “QObject()0“ ; 这个字符串都是些什么内容呀?有, Class Name, Signal Name, Slot Name, Property Name

46、。看到这些大家是不是觉得很熟悉呀,对啦,他们就是 Meta System所支持的最核心的功能属性了。 既然他们都是不等长的字符串,那么 Qt是如何来索引这些字符串,以便于在需要的时候能正确的找到他们呢?第三个成员正式登场了。 我们都知道,把一个 signal和 slot连接起来,需要使用 QObject类的connect方法,它的作用就是把一个 object的 signal和另外一个 object的 slot连接起来,以达到对象间通讯的目的。 connect 在幕后到底都做了些什么事情?为什么 emit一个 signal后,相应的 slot都会被调用?好了,让我们来逐一解开其中的谜团。 SIG

47、NAL 和 SLOT 宏定义 我们在调用 connect方法的时候,一般都会这样写: obj.connect( 我们看到,在这里 signal和 slot的名字都被包含在了两个大写的 SIGNAL和 SLOT中,这两个是什么呢?原来 SIGNAL 和 SLOT 是 Qt定义的两个宏。好了,让我们先来看看这两个宏都做了写什么事情: 这里是这两个宏的定义: # define SLOT(a) ”1#a # define SIGNAL(a) ”2#a 原来 Qt把 signal和 slot都转化成了字符串,并且还在这个字符串的前面加上了附加的符号, signal前面加了 2, slot前面加了 1。也

48、就是说,我们前面写了下面的 connect调用,在经过 moc 编译器转换之后,就便成了: obj.connect( 当 connect函数被调用了之后, 都会去检查这两个参数是否是使用这两个宏正确的转换而来的,它检查的根据就是这两个前置数字,是否等于 1或者是 2,如果不是, connect函数当然就会失败啦! 然后,会去检查发送 signal的对象是否有这个 signal,方法就是查找这个对象的 class所对应的 staticMetaObject 对象中所包含 的 d.stringdata所指向的字符串中是否包含这个 signal的名字,在这个检查过程中,就会用到d.data所指向的那一串整数,通过这些整数 值来计算每一个具体字符串的起始地址。同理,还会使用同样的方法去检查 slot,看响应这个 signal的对象是否包含有相应的 slot。这两个检查的任 何一个如果失败的话

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报