Introducing QScopedPointer

Qt usually takes the boring memory allocation and deallocation from you, either through its implicitly shared containers, or with QObject's parent child relationship model. But every once in a while, we need to allocate something on the heap, and then the stress starts - where do we delete it, and how do we make sure to not leak the memory?

To fix this problem, QScopedPointer was born. It will delete the object it is pointing to automatically when it goes out of scope:

void foo()
{
    QScopedPointer<int> i(new int(42));
    ...
    if (someCondition)
        return; // our integer on the heap will either be deleted here...
    ...
} // ... or here

A new exit condition in our function will not make it leak the integer that we allocated.

So how do we access the object that we are pointing to? QScopedPointer implements operator* and operator->, so you it can be accessed just like any other pointer:

    QScopedPointer<int> i(new int(42));
    *i = 43;

Some operators are missing by design, for example the assignment operator:

    QScopedPointer<int> i(new int(42));
    i = new int(43); // will not compile
    i.reset(new int(43)); // correct

We figured that "reset" looks scary enough to make the reader realize that the old object is deleted, and the QScopedPointer is now pointing to the new object.

Another operator that is missing by design is the operator T*() that would allow accessing the pointer directly. This prevents accidents like:

int *foo()
{
    QScopedPointer<int> i(new int(42));
    ...
    return i; // thankfully, this does not compile.
}

Do you see the mistake? The moment we return, our object will be deleted, because the scoped pointer goes out of scope. We would return a dangling pointer, potentially leading to a nasty crash. However, we can tell QScopedPointer that its job is done and that we take ownership of the heap object by calling take(). Our function might look like this:

int *foo()
{
    QScopedPointer<int> i(new int(42));
    ...
    if (someError)
        return 0; // our integer is deleted here
    return i.take(); // from now on, our heap object is on its own.
}

But what about memory allocated with malloc, or the operator new[] for arrays? For those cases, we introduced a second template parameter to QScopedPointer that defines the cleanup:

QScopedPointer<int, QScopedPointerPodDeleter> pod(static_cast<int *>(malloc(sizeof int)));

QScopedPointerPodDeleter (pod stands for "plain old data") will call free on the object if our QScopedPointer goes out of scope.

For convenience, there is a QScopedArrayPointer that defaults to deleting the object it is pointing to with the delete[] operator. It also features operator[] for convenience, so we can write:

void foo()
{
    QScopedArrayPointer<int> i(new int[10]);
    i[2] = 42;
    ...
    return; // our integer array is now deleted using delete[]
}

Note that if you have a reference counted object, you can use QExplicitlySharedDataPointer to ensure that an object is deleted correctly when its reference count goes to 0.

QScopedPointer and QExplicitlySharedDataPointer are already used all over the place in the Qt for S60 branch and will soon hit Qt's master branch. And the best part is that with the introduction of these smart pointers, we could remove lots of boring code and make Qt more readable without adding overhead - since all functions are inlined, the resulting binary using QScopedPointer is identical to the manual new/delete approach.

Happy developing :)


Blog Topics:

Comments