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

换句话说,强计数器记录有多少个 QSharedPointer 对象附加在这个特定的指针上;弱计数器记录同时存在多少个 QSharedPointer 和 QWeakPointer 实例数。所以你可以看到,当最后一个 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 对象生命。
http://www.devbean.info/2012/09/continue-using-qpointer/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值