在RobbiE的回答基础上,QPointer和QSharedPointer是两个互补的类,分别用于不同的功能。
QPointer及其注意事项
QPointer
是指向QObject
的弱指针。当所指向的对象被销毁时,它会将自身重置为零。它不是拥有指针:它永远不会删除对象本身,并且不能保证对象的存在。使用它可以避免出现悬空指针,该指针指向在其他地方管理所有权的对象。在每次使用前,请检查指针是否为空。如果对象在另一个线程中被销毁,则可能会遇到竞争条件:
if (pointer) /* another thread can destruct it here */ pointer->method();
QPointer
本身是线程安全的,但由于
QPointer
提供的API不足,使用它的代码永远无法保证线程安全。
QPointer
在主线程中始终可以与小部件对象一起使用,并且与已建立父子关系的小部件对象所拥有的对象一起使用。对象和其用户位于同一线程,因此在指针空检查和指针使用之间,对象不会被另一个线程处置:
QPointer<QLabel> label(...);
if (label) label->setText("I'm alive!");
如果您正在重新进入事件循环,需要小心。假设我们有以下代码:
QPointer<QLabel> label(...);
...
if (label) {
label->setText(...)
QFileDialog::getOpenFileName(...);
label->setText(...);
}
至少,在您调用主要不相关的代码(例如,发出信号(任何人都可以对其做出任何反应!),返回一个事件循环重新进入的调用,如 exec
等)后,您需要重新检查 QPointer
。这也是为什么阻塞调用不好的原因:您永远不应该使用它们。
QPointer<QWidget> widget(...);
...
if (label) {
label->setText(...);
QFileDialog::getOpenFileName(...);
}
if (label) {
label->setText(...);
...
}
QSharedPointer和QWeakPointer
本节内容仅供参考。在现代代码中,应该使用std::shared_ptr
和std::weak_ptr
,不需要任何保留。截至2018年,它们已经存在于C++中7年。
QSharedPointer
是一个拥有指针。它的工作方式类似于Java和CPython中的变量,或者类似于std::shared_ptr
。只要有至少一个QSharedPointer
指向一个对象,该对象就会被保留。当最后一个QSharedPointer
被销毁时,对象也会被销毁和删除。
QWeakPointer
是QSharedPointer
的表兄弟。它是非拥有型指针。它跟踪由QSharedPointer
持有的对象是否仍然存活。当拥有对象的最后一个QSharedPointer
消失时,它将重置为nullptr
。它可以被看作是对非QObject
类的QPointer
的一般化。使用QWeakPointer
的唯一安全方法是将其转换为QSharedPointer
。当您持有共享指针时,对象将被保证保持活动状态。
QPointer
类似于QObject
的QWeakPointer
,但不需要存在QSharedPointer
。
在堆上分配的对象和由其他机制管理生命周期的对象使用QSharedPointer
是错误的。例如,对于具有父项的QObject
,拥有QSharedPointer
是错误的。对象的父项将删除它,并且您将得到一个悬空的QSharedPointer
!Qt具有一些内置检查,当发生这种情况时会发出警告,但此时为时已晚,未定义的行为已经发生。
QScopedPointer
本节内容仅供参考。应该使用std::unique_ptr
,没有任何保留。截至2018年,它已经存在于C++中7年。
QScopedPointer
和std::unique_ptr
一样,是一个独占型指针。它的工作就是在其超出范围时删除所持有的对象。C++11的unique_ptr
名称非常适合:它是一个唯一的指针,试图复制这样的指针是错误的。始终只有一个QScopedPointer
拥有一个给定的对象,并且它不与其他智能指针类型合作。您可以通过调用data
方法获取底层对象的原始指针。
std::auto_ptr
这个指针是为了解决C++98/03中缺乏移动语义而尝试的。由于其破碎的复制语义,使用这个类应该被视为一个错误。使用std::unique_ptr
或std::shared_ptr
——如果只需要可移动性,则使用前者;如果需要多个副本共存,则使用后者。