Qt Core模块的主要内容,来源: https://doc.qt.io/archives/qt-5.12/qtcore-index.html
对象模型
标准C++对象模型为对象范例提供了高效的运行时支持。但是它的静态性质在某些领域不够灵活。图形用户界面编程是一个既需要运行时效率又需要高度灵活性的领域。Qt通过将C++的速度与Qt对象模型的灵活性相结合满足了这一需求。
Qt向C++添加以下特性:
- 无缝的对象间通信机制:信号与槽
- 可查询和可设计的对象属性
- 事件和事件过滤器
- 上下文相关的用于国际化的字符串翻译
- 复杂的时间间隔驱动的定时器集成一个事件驱动的GUI内的多个任务
- 等级制、可查询的对象树通过一种自然的方式组织对象所有权
- 在对象被销毁时自动设置为0的受保护的指针(QPointer),而不像一般C++指针变成悬挂指针
- 跨库边界的动态转换
- 支持创建自定义类型
这些Qt特性中的许多都是用标准C++技术实现的,并基于对QObject的继承。其他的,如对象通信机制和动态属性系统,需要Qt自己的元对象编译器(moc)提供的元对象系统。
元对象系统是一个C++扩展,它使该语言更适合真正的组件GUI编程。
重要的类
类型 | 说明 |
---|---|
QMetaClassInfo | 类的额外信息 |
QMetaEnum | 枚举器有关的元数据 |
QMetaMethod | 成员函数有关的元数据 |
QMetaObject | 关于Qt对象的元信息 |
QMetaProperty | 属性有关的元数据 |
QMetaType | 管理元对象系统中的命名类型 |
QObject | 所有Qt对象的基类 |
QObjectCleanupHandler | 监视多个QObjects的生存期 |
QPointer | 为QObject提供受保护指针的模板类 |
QSignalBlocker | QObject::blockSignals()异常安全的封装 |
QSignalMapper | 捆绑来自可识别发送者的信号 |
QVariant | 类似常见Qt数据类型的合集 |
Qt对象:身份(Identity )与值(Value)
上面为Qt对象模型列出的一些附加功能要求我们将Qt对象视为身份,而不是值。值被复制或分配;身份被克隆。克隆意味着创建一个新的身份,而不是旧身份的拷贝。例如,双胞胎有不同的身份。他们可能看起来一模一样,但他们有不同的名字,不同的地点,可能有完全不同的社交网络。
克隆标识比复制或分配值更复杂。
对于一个Qt Object
- 或许有一个唯一的QObject::objectName(),如果复制一个Qt Object,对象名应该设置为什么?
- 有一个对象层次结构中的位置,如果复制一个Qt Object,对象位置应该设置为什么?
- 与其他对象有信号/槽的关联,如果复制一个Qt Object,这些关联是否也应传输过去?
- 可以在运行时添加新属性而不是在定义c++类时内声明,如果复制一个Qt Object,是否应该包含这些新增的属性?
由于这些原因,Qt对象应该被视为身份,而不是值。标识是克隆的,而不是复制或分配的,克隆标识比复制或分配值更复杂。因此,QObject和QObject的所有子类(直接或间接)都禁用了它们的复制构造函数和赋值运算符。
对象树和所有权
概述
QObjects在对象树中组织自己。当你创建一个以另一个对象为父对象的QObject时,它会被添加到父对象的children()列表中,并在父对象被删除时删除。事实证明,这种方法非常适合GUI对象的需要。例如,QShortcut(键盘快捷键)是相关窗口的子窗口,因此当用户关闭该窗口时,快捷键也会被删除。
QQuickItem是Qt Quick模块的基本视觉元素,它继承自QObject,但有一个不同于QObject父级的视觉父级概念。项的视觉父级可能不一定与其对象父项相同。
Qt-Widgets模块的基本类QWidget扩展了父-子类关系。一个子类通常会成为一个子部件,即它显示在父部件的坐标系中,并被父部件的边界进行图形剪裁。例如,当应用程序在关闭消息框后删除消息框时,消息框的按钮和标签也会被删除,正如我们所希望的那样,因为按钮和标签是消息框的子级。
您也可以自己删除子对象,这些子对象将自己从父对象中删除。例如,当用户删除工具栏时,可能会导致应用程序删除其QToolBar对象之一,在这种情况下,工具栏的QMainWindow父对象会检测到更改并相应地重新配置其屏幕空间。
当应用程序的外观或行为异常时,调试函数QObject::dumpObjectTree()和QOobject::dumpObjectInfo()通常很有用。
QObjects的构造和销毁顺序
当在堆上创建QObjects(即使用new创建)时,可以按任何顺序从它们构建树,然后,可以按任意顺序销毁树中的对象。当删除树中的任何QObject时,如果该对象有父对象,则析构函数会自动从其父对象中删除该对象。如果对象有子对象,则析构函数会自动删除每个子对象。无论销毁顺序如何,都不会删除任何QObject两次。
当在堆栈上创建QObjects时,同样的行为也适用。通常情况下,销毁顺序仍然不会带来问题。考虑以下代码段:
int main()
{
QWidget window;
QPushButton quit("Quit", &window);
...
}
父窗口windows和子窗口quit都是QObject,因为QPushButton继承了QWidget,而QWidget继承了QObject。这段代码是正确的:quit的析构函数不会被调用两次,因为C++语言标准(ISO/IEC 14882:2003)规定本地对象的析构因函数的调用顺序与构造函数的调用顺序相反。因此,首先调用子对象quit的析构函数,并在调用window的析构函数之前将其自身从其父对象window中移除。
但现在考虑一下,如果我们交换构建顺序会发生什么,如第二个代码段所示:
int main()
{
QPushButton quit("Quit");
QWidget window;
quit.setParent(&window);
...
}
在这种情况下,析构函数的顺序造成了一个问题。父级的析构函数是先调用的,因为它是后创建的。然后它调用其子级quit的析构函数,这是不正确的,因为quit是一个局部变量。当quit随后离开作用域时,它的析构函数会被再次调用,这一次是正确的,但损坏已经造成了。