为什么 Qt 使用原始指针?
Why does Qt use raw pointers?
在使用纯 C++ 编写大量代码后,我最近又回到了 Qt/C++ 编程。
浏览 Whosebug 时,我经常会看到像 "Why use pointers?" 这样的帖子,在大多数情况下,答案的要点是“如果可以避免,就不要使用它们".
在用 C++ 编写代码时,我现在主要尝试使用由 (const
) 引用传递的堆栈变量,或者,如果有必要,std::shared_ptr
resp。 std::unique_ptr
需要的地方。
回到 Qt,我发现所有这些“原则”显然都被完全忽略了。
我知道 Qt 使用自己的内存管理来处理 raw 指针,但这是我的问题:
他们为什么不至少使用 shared_ptr
或 unique_ptr
,特别是因为他们甚至有自己的实现 QSharedPointer
?
Qt 因为版本 4.x 是围绕在 C++ 环境中模仿 Java 的框架思想而设计的,使用 C++98 手段。它不是 RAII 交互方法,而是建立“所有者”-“从属”关系,在框架的术语中是“parent”和“child”。更多的是,Qt 使用了 PIMLP 的概念——私有实现。 QObject
您操作的不是正在发生的事情的真实表示,它们是完全隐藏的内部实现的接口。
根据设计,您必须创建 child 元素的 QObject-derived object 并将所有权传递给所属的 object 。例如。其中 window 是所有者,window 中的 Button 将是“奴隶”object。当所有者被删除时,所有从属于它的 objects 也将被删除。所有QObject
都是thread-aware,但是QWidgets
只能在主线程中工作。这将创建一个 non-owning 指针:
QWidget *myWidget = new QWidget(mainWindow);
在这种情况下,mainWindow
将拥有 QWidget
个实例。但是这个是拥有?
QWidget *myWidget = new QWidget;
不是。它仍然归 QApplication 所有。
QObjectDerivedClass *myWidget = new QObjectDerivedClass;
它是一个拥有指针,但是这个 object 被注册为存在于我们的框架中。更重要的是,如果为实例分配了名称,则可以找到任何实例,存储 QObjects 以访问它们只是一种缓存优化。
所有 QObjects
和 QWidgets
都是全局注册的并且是可迭代的。随着 QApplicationCore
实例的销毁,所有顶级 QWidgets 都将被释放。至少在 Qt 4.x 版本中存在未记录的例外情况,即 QDesktopWidget
object 被忽略,即使它们是 top-level 小部件。所以,如果一个 QMainWindow
被迫出现在某个屏幕上成为它的 child,它就不会被销毁。
现在开始使用 signal-slot 个连接。在 GUI 中,某些处理程序在 parent-child 连接建立后立即开始工作,但您可以添加新的处理程序。如果 child object 在 QEventLoop
创建的消息泵的开头和结尾之间被删除,您的程序可能会遇到 UB。为避免这种情况,您必须调用 deleteLater()
以在指定时刻标记 object 进行删除。线程间的信号处理是分开进行的。实际上,主事件循环是 GUI 中唯一与其他线程同步的部分。
由于一些受支持的嵌入式平台强加了如此复杂的结构和已经存在的在一个线程中工作的要求,与可能对性能产生的影响相比,需要在 GUI 框架内使用智能指针可以忽略不计。
在 C++11 之前 Qt 有 QPointer
(并且仍然得到它),它知道 QObject 是否仍然存在或不使用类似于 owner-child 交互的机制。
此设计早于 C++11,QSharedPointer
仅在此之后出现,以填补用户要求维护 user-defined 数据模型的利基市场。它不支持某些功能,例如,您不能像 ISO 版本那样拥有它的原子版本,在 5.x 的最后一个版本之前它只是部分原子的。 QAtomicPointer
既不是 QPointer
也不是 QSharedPointer
,它的作用类似于 std::atomic<QObject*>
。 QSharedPointer
虽然允许使用自定义 non-standalone 删除器,包括调用 deleteLater()
:
QSharedPointer<MyDataQObject> objPtr { new MyDataQObject, &QObject::deleteLater };
假设 MyDataQObject
派生自 QObject
,objPtr
将在托管 object 的上下文中调用方法 deleteLater()
而不是使用 delete
在销毁或重置时在托管指针上。
在使用纯 C++ 编写大量代码后,我最近又回到了 Qt/C++ 编程。
浏览 Whosebug 时,我经常会看到像 "Why use pointers?" 这样的帖子,在大多数情况下,答案的要点是“如果可以避免,就不要使用它们".
在用 C++ 编写代码时,我现在主要尝试使用由 (const
) 引用传递的堆栈变量,或者,如果有必要,std::shared_ptr
resp。 std::unique_ptr
需要的地方。
回到 Qt,我发现所有这些“原则”显然都被完全忽略了。
我知道 Qt 使用自己的内存管理来处理 raw 指针,但这是我的问题:
他们为什么不至少使用 shared_ptr
或 unique_ptr
,特别是因为他们甚至有自己的实现 QSharedPointer
?
Qt 因为版本 4.x 是围绕在 C++ 环境中模仿 Java 的框架思想而设计的,使用 C++98 手段。它不是 RAII 交互方法,而是建立“所有者”-“从属”关系,在框架的术语中是“parent”和“child”。更多的是,Qt 使用了 PIMLP 的概念——私有实现。 QObject
您操作的不是正在发生的事情的真实表示,它们是完全隐藏的内部实现的接口。
根据设计,您必须创建 child 元素的 QObject-derived object 并将所有权传递给所属的 object 。例如。其中 window 是所有者,window 中的 Button 将是“奴隶”object。当所有者被删除时,所有从属于它的 objects 也将被删除。所有QObject
都是thread-aware,但是QWidgets
只能在主线程中工作。这将创建一个 non-owning 指针:
QWidget *myWidget = new QWidget(mainWindow);
在这种情况下,mainWindow
将拥有 QWidget
个实例。但是这个是拥有?
QWidget *myWidget = new QWidget;
不是。它仍然归 QApplication 所有。
QObjectDerivedClass *myWidget = new QObjectDerivedClass;
它是一个拥有指针,但是这个 object 被注册为存在于我们的框架中。更重要的是,如果为实例分配了名称,则可以找到任何实例,存储 QObjects 以访问它们只是一种缓存优化。
所有 QObjects
和 QWidgets
都是全局注册的并且是可迭代的。随着 QApplicationCore
实例的销毁,所有顶级 QWidgets 都将被释放。至少在 Qt 4.x 版本中存在未记录的例外情况,即 QDesktopWidget
object 被忽略,即使它们是 top-level 小部件。所以,如果一个 QMainWindow
被迫出现在某个屏幕上成为它的 child,它就不会被销毁。
现在开始使用 signal-slot 个连接。在 GUI 中,某些处理程序在 parent-child 连接建立后立即开始工作,但您可以添加新的处理程序。如果 child object 在 QEventLoop
创建的消息泵的开头和结尾之间被删除,您的程序可能会遇到 UB。为避免这种情况,您必须调用 deleteLater()
以在指定时刻标记 object 进行删除。线程间的信号处理是分开进行的。实际上,主事件循环是 GUI 中唯一与其他线程同步的部分。
由于一些受支持的嵌入式平台强加了如此复杂的结构和已经存在的在一个线程中工作的要求,与可能对性能产生的影响相比,需要在 GUI 框架内使用智能指针可以忽略不计。
在 C++11 之前 Qt 有 QPointer
(并且仍然得到它),它知道 QObject 是否仍然存在或不使用类似于 owner-child 交互的机制。
此设计早于 C++11,QSharedPointer
仅在此之后出现,以填补用户要求维护 user-defined 数据模型的利基市场。它不支持某些功能,例如,您不能像 ISO 版本那样拥有它的原子版本,在 5.x 的最后一个版本之前它只是部分原子的。 QAtomicPointer
既不是 QPointer
也不是 QSharedPointer
,它的作用类似于 std::atomic<QObject*>
。 QSharedPointer
虽然允许使用自定义 non-standalone 删除器,包括调用 deleteLater()
:
QSharedPointer<MyDataQObject> objPtr { new MyDataQObject, &QObject::deleteLater };
假设 MyDataQObject
派生自 QObject
,objPtr
将在托管 object 的上下文中调用方法 deleteLater()
而不是使用 delete
在销毁或重置时在托管指针上。