徐红伟@百香果科技
QObject类是所有Qt对象的基类。是Qt对象模型(Object Model)的基础,该模型最核心的特点是对象间的无缝通信,即信号和槽(signals and slots)。
Signals和Slots机制
signals 和 slots是用于「对象间」通信的。
signal像(但不是)函数指针,更像C#中的事件,槽就是「函数」。
signal-slot的功能比函数指针更多,比如可以多对一,和一对多的连接。代价是调用速度略慢。
用QObject的 static method:connect()和disconnect()进行信号和槽的连接/解连接。(这解释了为何使用信号-槽的Class中必须加入 Q_OBJECT宏)
自动内存管理
QObjects使用「对象树」(object-tree)进行内存管理,啥子意思呢?
QObject(QObject *parent= nullptr)
QPushButton(QWidget *parent = nullptr)
QChart::QChart(QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = ...)
其实QObject在的构造函数里有个parent参数。如果创建时指定了parent参数,对象就会自动添加到parent的 children() 的列表中。此时parent对象就会接管子对象。就是说parent对象销毁的时候,会自动delete所有子对象,不用怕「内存泄露」。
其实Qt的内存管理是「半自动」的(参考[3]),delete的子对象是在栈上分配的,那么delete就会出问题。所以Qt推荐子对象用new()函数生成,而不是直接定义实体。
[virtual] QObject::~QObject()" All child objects are deleted. If any of these objects are on the stack or global, sooner or later your program will crash."这是QObject析构函数的描述。如果QObject的child objects在栈上或是全局变量,则程序迟早会崩溃。
这就像一棵大树,只要把根砍了(销毁了),会自动销毁它的枝叶。
QObjects通过 event() 函数接收事件,并可以过滤事件,参考 installEventFilter() and eventFilter()。还可以通过 childEvent()来捕获子对象的事件。
其他
当QObject对象被销毁时,会发射 destroyed() 信号,可以用之来监测信号的生命周期 。
最后,QObject提供一个定时器 QTimer,用于定时。
如果对象要实现signals-slots或properties,就必须使用 Q_OBJECT宏。推荐在所有QObject的派生类中添加 Q_OBJECT宏,无论是否用到signals-slots特性。
所有的QWidgets对象继承于QObject,用isWidgetType()函数能快速查看对象是否为widget。
线程亲和性(Thread Affinity)
QObject 具有线程亲和性,即它存在于特定的线程里。当它接收到 queued signals 或 posted events 时,slots 或 event handlers 在 QObject 所在的线程里运行。(QObject 对象的 slot成员函数在其被创建的线程里运行,而不是发射信号的线程了)。
注意:如果QObject 没有亲和的线程(即 thread() 函数返回 0),则它所在的线程没有运行事件循环,因此它就无法接收到 queued signals或posted events.
默认情况下,QObject生存在创建它的线程下。可以用 thread() 函数查看它所在线程,也可以用 moveToThread()改变所在线程。
所有的 QObjects 都必须和其parent在同一个线程里,因此:
1. 如果两个QObject在不同的线程里,则不能用setParent() 建立父子关系。
2. 当 QObject 移动到另一个线程里后,其所有子对象(指定parent关系的)也自动移动到相同线程。
3.如果QObject有父对象,则对其进行moveToThread()时会失败。
4.如果QObject在 QThread::run()中创建,则它不能成为QThread的子对象,因为QThread不在QThread::run()线程里。
注意:QObject的成员变量不会自动成为其子对象。只有现实调用setParent()或再子对象的构造函数里指定父对象parent,才能建立父子关系。因此把QObject移动到一个线程时,他的成员变量(非子对象)不会移动。
无拷贝构造函数(copy constructor)和=操作
在设计QObject时,特意取消了「拷贝构造函数」和 = 操作。
所在在传递QObjet及其子对象时,要传递指针。在容器类中,也只能存储其指针。
国际化
所有QObject的子类都支持Qt的翻译特性,让app的UI可以翻译成不同语言。为了使UI上的字符可翻译,必须用 tr() 函数包起来,详细内容见Writing Source Code for Translation。
[1]参考Qt官方文档:“QObejct”
[3].Qt浅谈之一:内存泄露(总结)