首页 Qt 继续使用 QPointer

继续使用 QPointer

5 4

原文地址:http://www.macieira.org/blog/2012/07/continue-using-qpointer/

在 Qt 5 的早期开发工作中,我们决定放弃QPointer,使用更现代化的QWeakPointer作为替代。现在我们收回这个决定,所以,请在需要的时候继续使用QPointer。另外,不要使用QWeakPointer,除非需要结合QSharedPointer一起使用。

为了了解这其中的原因,我们需要回顾一下历史。

理解QPointer

大约 3 年之前,我写过一篇 关于 Qt 中的智能指针的文章。在那篇文章中,我们讨论了关于QPointer存在某些不恰当的语义,以及它应当被废弃。我甚至声称,QPointer太慢了。

在 Qt 4 中,QPointer缓慢的原因在于其实现方式。它是 Qt 3 的QGuardedPtr的直接子类。当你为一个对象创建QGuardedPtr时,Qt 简单地将指针及其守卫添加到一个全局的散列表。在 Qt 4 中,其实现是一样的,除了它需要请求一个全局的互斥锁。(如果你不了解 Qt 3,这里需要说明一点,在 Qt 3 中,QObject 不能在主线程之外使用。)

这意味着,在 Qt 4 中创建QPointer实例的时候,需要请求一个全局互斥锁,然后将这个对象插入到一个全局的散列表,这一过程可能需要调用散列函数,这取决于这个散列表里面究竟有多少项目。当这个QObject销毁时,它需要遍历整个散列表,通知所有观察者说,该对象已经销毁。

再来说说QWeakPointer

在 Qt 4.5 中,我们引入了QSharedPointerQWeakPointer两个类,提供更好的性能。这两个类的实现是使用一种具有引用计数的引用计数器 reference-counted reference-counter的技术。也就是说,这两个类中都有一个私有数据,包含了两个引用计数器:一个强计数器,一个弱计数器。强计数器记录指针的生命,当其值为 1 或者大于 1 时,意味着这个对象存在,如果是 0 则指出该对象已经被销毁。弱计数器则控制这个私有数据自己的生命。

换句话说,强计数器记录有多少个QSharedPointer对象附加在这个特定的指针上;弱计数器记录同时存在多少个QSharedPointerQWeakPointer实例数。所以你可以看到,当最后一个QSharedPointer实例销毁时,这个指针也就被销毁了。

在 Qt 4.6 中,我们添加了一个新特性,允许使用QWeakPointer直接追踪QObject实例数,无需通过QSharedPointer。当我们试图寻找改善QPointer的性能时,我意识到,“具有引用计数的引用计数器”是最好的解决方案——事实上,因为不使用QSharedPointer进行追踪,“具有引用计数的 bool 类型(reference-counted boolean)”已经足够,但因为我们并没有QAtomicBool这种类型,所以我们正常地使用引用计数器也是可以的。

解决方案很简单:如果你创建了守卫的字一个实例,它会分配一个私有块,设置强计数器,指示对象存活。当对象删除时,直接将强计数器置零。使用弱计数器则允许我们共享私有块的所有权,所以是极快的。

来到 Qt 5…

当我们开始 Qt 5 时,显然之前实现的QPointer应当废弃。我们不想再将这种古老的实现维护3-6年,或者更长,直到 Qt 6。当我们承诺尽可能多的维持与 Qt 4 源代码级的兼容,所以我们没有删除这个类。

去年11月,一个开发人员简单地在QWeakPointer的基础之上重写了QPointer,将其标记为废弃。通过保留原始 API,改成新的实现,我们有了一个快速的QPointer。但是我们没有去除“废弃”标记,因为我们认为我们已经有“太多的智能指针”。

今年3月,我们开始尝试清理标记了废弃的 Qt 代码,我们意识到QPointer到处都有使用。因为我们已经有了一个足够快速的实现,所以没有任何理由还让编辑器保留使用废弃类的警告。这导致了将QPointer的废弃标记去除。

最后,一个半月之前,另一只鞋丢掉了。如果这些智能指针都有着恰当地 API,并且这些类都能够正确的工作,那么,有关“太多的智能指针”的争论就已经不合适了。我们意识到,重载QWeakPointer的设计目的只会让其 API 更糟,导致人们不知道何时使用其成员函数。因此,我们删除了有关“不结合QSharedPointer使用QWeakPointer”的讨论。

结论

这意味着,你应当在你的代码中继续使用QPointer。不幸的是,如果你还在为 Qt 4 开发程序,这意味着你的程序可能不像想象中那么快,但至少它是清晰的。

这也意味着,QWeakPointer::data()函数已经被废弃,你不应该继续使用它。

Qt 的智能指针类可以归纳如下:

分组描述
共享数据QSharedDataPointer, QExplicitlySharedDataPointer隐式或者显式地共享数据(不共享指针)。也被称为“侵入式指针”。
共享指针QSharedPointer,
QWeakPointer
线程安全的共享指针,类似 C++11 的std::shared_ptr,只不过具有良好的 Qt API。
范围指针QScopedPointer,
QScopedPointerArray
为了 RAII 目的:获取指针所有权,保证其能够在超出作用域的时候恰当地销毁。非共享的。
追踪QObject对象QPointer追踪给定的 QObject 对象生命。

5 评论

Kiwee 2013年4月16日 - 17:24

。。。。。这儿看的不太懂。。。

回复
豆子 2013年4月17日 - 09:07

只看结论…

回复
轨迹 2013年5月29日 - 09:25

这里有点不太懂,我只是知道c++11那里的是这么用的。大部分情况使用shared_ptr,有相互引用的时候其中一个类使用weak_ptr。
既然qt是用了一种自己的管理方式来管理内存,QPointer还是普通的c++代码使用智能指针那么重要吗?

回复
豆子 2013年5月30日 - 10:54

Qt 使用了半自动化的内存管理,但是有时候也不得不使用智能指针做一定的处理。比如并不是所有的类都是 QObject 的子类,或者有某些原因不能找到一个合适的充当 parent 的类,此时就可以选择智能指针了。

回复
轨迹 2013年5月30日 - 10:59

回复

发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2