«

»

Jul 10

Continue using QPointer

Early in the Qt 5 development cycle, we had made the decision to deprecate QPointer and replace it with the more modern QWeakPointer. That decision is now reversed, so please continue using QPointer where you were using them. Moreover, don’t use QWeakPointer except in conjunction with QSharedPointer.

To understand the reason behind this back and forth, we need to go back a little in history.

Understanding QPointer

Almost 3 years ago, I wrote a blog about the smart pointer classes in Qt. In that blog, I talked about how QPointer had some broken semantics and how it should be deprecated. I even mentioned that it was slow.

The reason it is slow in Qt 4 is its implementation. It’s a direct descendant of the Qt 3 QGuardedPtr and Whenever you created a QGuardedPtr for a given object, Qt would simply add the pointer and the guard to a global hashing table. In Qt 4, the implementation is the same, except that now it required locking a global mutex. (For those who don’t remember Qt 3, QObject could not be used outside the main thread there).

That means that creating a QPointer in Qt 4 requires locking a global mutex and inserting an item into a global hash, which may involve rehashing depending on how many items it holds. And when that QObject was destroyed, it would need to iterate over the hash and notify all the watchers that the object had died.

Enter QWeakPointer

With Qt 4.5, I introduced QSharedPointer and QWeakPointer, which are a lot more efficient. The way that those two communicate is by way of a “reference-counted reference-counter”. That is, the private data common to those two classes contains basically two reference counters: the strong and the weak one. The strong counter counts the lifetime of the pointer, where a value of 1 or higher indicates that the object is still alive, whereas a zero indicates that it’s deleted. The weak counter controls the lifetime of the private data itself.

In other words, the strong counter counts how many QSharedPointer objects are attached to this particular pointer, whereas the weak counter counts both QSharedPointer and QWeakPointer instances. So you see how the last QSharedPointer instance being destroyed causes the pointer to be deleted too.

With Qt 4.6, I added a feature that allowed one to use QWeakPointer to track QObject instances directly, without going through QSharedPointer. When trying to figure out how to optimise QPointer, I realised that the “reference-counted reference counter” is the best solution — actually, for tracking without QSharedPointer, a “reference-counted boolean” would be enough, but since we didn’t have QAtomicBool, we regular reference counter would do just fine.

The solution is simple: if you create a the first instance of the guard, it allocates this private block and sets the strong counter to indicate that the object is alive. When the object is deleted, it simply sets the strong counter to zero. The use of the weak counter allows us to share the ownership of this private, so it’s quite fast.

And in Qt 5…

When we started working on Qt 5, it was clear that the previous implementation of QPointer had to go. We don’t want to keep old cruft for another 3-6 years, or however long it’s going to take until we reach Qt 6. But we also promised to maintain source compatibility as much as possible with Qt 4, so we couldn’t just remove the class.

Instead, last November, one developer simply rewrote QPointer on top of QWeakPointer and deprecated it. By keeping the old API and coupling it with the new implementation, we suddenly had a fast QPointer. But the deprecation stayed, because we thought we had “too many smart pointer classes” (see the title of my blog).

In March, when we started to try and clean up the Qt code base of our own deprecated classes, we realised that QPointer is used just about everywhere. Since we had a properly fast implementation, there was actually no good reason to keep generating compiler warnings about the use of that class. That led to QPointer being un-deprecated.

Finally, a month and a half ago, the other shoe dropped. The argument of “too many classes” is not valid if it’s trumping over having proper API and classes that work correctly. What we realised is that overloading QWeakPointer‘s purpose was making its API worse instead, leading to doubts about when to use some of its member functions. Therefore, we deprecated instead QWeakPointer use without QSharedPointer.

Conclusion

That means you should continue using QPointer in your code. Unfortunately, if you’re still targetting Qt 4, it means your code will not be as fast as it could be, but at least it will be clean.

It also means the QWeakPointer::data() member is deprecated and you should not use it.

Qt’s smart pointer classes are neatly grouped now as follows:

GroupClassesDescription
Shared dataQSharedDataPointer, QExplicitlySharedDataPointerSharing of data (not of pointers), implicitly and explicitly. Also known as “intrusive pointers”.
Shared pointersQSharedPointer,
QWeakPointer
Thread-safe sharing pointers, like C++11′s std::shared_ptr only with a nice Qt API
Scoped pointersQScopedPointer,
QScopedPointerArray
For RAII usage: takes ownership of a pointer and ensures it is properly deleted at the end of the scope. No sharing.
Tracking QObjectsQPointerTracks the lifetime of a given QObject instance

1 comment

  1. avatar
    Philippe

    Thanks for the info. Just a short note based on my experience: I originally used QPointer, then started using QWeakPointer as replacement.
    Unlike for QWeakPointer, there is a T* QPointer::operator->()
    For this reason, QPointer was very easy to use, exactly as a normal pointer, and I originally found the need to use QWeakPointer::data() cumbersome.
    But with months going by, I found an advantage to this: the forced use of data() helped me to have a better overview of my code by identifying and keeping in mind where those smart pointers are used.
    Else it’s too easy to forget.

Comments have been disabled.

Page optimized by WP Minify WordPress Plugin