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 ![]()
31 Responses to “Introducing QScopedPointer”
I don’t know how I programmed without it!! Ok, I do: with memory leaks. Or tedious coding to make sure everything was deallocated.
I hope this is in 4.6!
:thumbsup:
It would have been nice to mention that third party libraries, such as boost, have had scoped pointer for quite sometime, and that they also have the facilities demonstrated here.
But it’s great now that I don’t have to include boost for this single functionality. What about the difference from a QSharedPointer to a QScopedPointer? Only overhead?
You cannot take()/release ownership from a SharedPointer.
A common way to use auto_ptr/ScopedPointer is this:
Foo* gimmeAFoo() {
std::auto_ptr foo( new Foo );
// do stuff with foo
if ( someError ) // auto_ptr will delete the object
throw SomeError( “error! Get kids from the street!” );
if ( anotherError ) // auto_ptr will delete the object
return 0;
// to more stuff
return foo.release(); // take ownership from the auto_ptr and give it to the caller
}
That’s not possible with a SharedPointer (one can return a QSharedPointer though)
Could you please explain differences to std::auto_ptr?
It would also be nice if Qt’s flavor of smart pointer could share ownership with QObjects somehow. So for example you could create shared pointer to a widget that has a parent and expect it to survive parent’s destruction.
Will the object be deleted if it goes out of scope because an exception was thrown?
IMHO “take” is an unfortunate choice: std::auto_ptr has a similar method that’s called ‘release’.
Semantically, these two verbs are just about opposite, yet describe two similar actions for the two classes.
Other than that, is there any non-cosmetic difference from boost::scoped_ptr ? Oh and since there is a scoped_array, I imagine you’ll introduce QScopedArray which calls delete [] on its member ?
I’ve been using the standard libraries auto_ptr (or however it is written) for some time, which has essentially the same properties. But introducing the ability to use QScopedArrayPointer with arrays is really great, as that was missing in the standard library AFAIC. Having the delete-part as a template-parameter is a good option
Could this pointer also be “abused” or generalized for the resource-acquisition-is-initialisation technique? Say when you need to deal with an API without C++ bindings, like getting a handle to something and having the pointer object close the handle once it is destroyed?
Kinda like:
QScopedSomething someHandle(open_some_device(”/dev/something”));
do_something_with_device(someHandle.get())
if (something_bad)
return; // do not worry about the handle, will be closed automatically!
Mike: I’m sure it is possible to make your own deallocator and model it after QScopedPointerPodDeleter. It probably has one function that takes void* and calls free() on it. QScopedPointer destructor then just calls this function (or operator(void*)).
I’m sure it is pretty similar to custom deallocators in boost smart pointers.
The question was already asked : wasn’t QSharedPointer sufficient ?
The differences I see are :
- QSharedPointer is (atomically) ref counted, QScopedPointer is not.
- QScopedPointer provides an additional template argument to enable custom deletion, however QSharedPointer supports an additional Deleter argument for the same purpose.
- QScopedPointer has an explicit reset() function, whereas QSharedPointer must be reaffected but the result is identical.
What am I missing here?
Occam’s razor would suggest, then, that the speed difference is exactly the point — it’s even explicitly mentioned in the post. (From what I’ve heard, atomic operations aren’t cheap, though I don’t know how they compare to mallocs.)
Anyway, one thing that could’ve been touched upon is an actually /realistic/ use case — if you’re just going to be allocating something for the duration of a function call, you would’ve just used a stack variable in the first place. One thing I can imagine this is going to be more useful for is objects allocating things in the constructor which they would then need to deallocate in the destructor (d-pointers are a great example), but can now just hold a QScopedPointer to instead… what else?
std::shared_ptr in the C++0x standard has been shipped for long in Visual C++ and Gcc. There is also std::unique_ptr which supports move semantics.
Cyril: You cannot release()/take() a SharedPointer, to take over ownership.
That means you cannot:
Foo* createFoo() {
std::auto_ptr foo( new Foo );
… do stuff that might throw, or have early returns
return foo.release();
}
or
void Bar::whatever() {
std::auto_ptr foo( new Foo );
… act on foo. might throw or just return
// success, commit changes
m_foo = foo.release();
}
I think a smart pointer that drives a Garbage Collector in the background would be a much better Idea then this Shared Pointer/Scoped Pointer classes. Because a Garbage Collector could detect cylcles in the Reference chain and you would really never have to worry about memmory leaks if you would only use this smart Pointer. It would be great if Qt could provide something like this.
illisius: This is a method used to provide exception safety; if an exception is raised before your function returns, the allocated memory is already cleaned up. If you allocate 10 objects in sequence and put them all in RAII classes like this one, then you don’t need several cleanup functions after checking whether each new/malloc succeeded.
hunt0r: Garbage collectors are tricky and destroy expectations of predictable destruction. Worse yet, even with mark & sweep, some cases cannot be handled by a garbage collector.
Overall, this seems like a poor man’s copy of boost scoped_ptr and scoped_array; there is no reason that the destruction method needs to be a template parameter, since one can use type erasure by passing in information about the destruction method into the constructor. It seems like Qt seems to reinvent the wheel more and more often nowadays, even when there exist free license-compatible mature alternatives elsewhere.
Nice to see Qt providing smart pointers. So, lemme check whether i got it right:
QScopedPointer -> std::unique_ptr of C++0x
QExplicitlySharedDataPointer -> std::shared_ptr of C++0x, boost::shared_ptr
QSharedDataPointer -> something else. COWs the pointed data.
Not sure, but i think the similarity of “QSharedDataPointer” and “std::shared_ptr” with quite different semantics could cause confusion.
Right. It begins to be quite a mess with all these pointer-wrapper classes around :
QPointer, QScopedPointer, QExplicitlySharedDataPointer, QSharedDataPointer, QSharedPointer, QWeakPointer, …
Maybe it would be a nice idea to sum up all of these and their behavior/features in a dedicated doc page. There are already elemens of comparison spread across the Qt doc. I am pretty sure vohi has such a hint on his doc roadmap!
AFAIK:
QPointer - pointer that sets itself to 0 on destruction
QScopedPointer - auto_ptr
QSharedPointer - shared_ptr
Q(Explicitly)SharedDataPointer - COW together with QSharedData
@Razvan Petru, oh this makes sense now. I didn’t know that there is a QSharedPointer ![]()
I don’t see the point in reimplementing something like a QScopedPointer this late in the game for a toolkit such as Qt. The C++ standard libraries already include this sort of functionality in std::auto_ptr (but without the interesting move semantic copy constructor) and there are also other scoped pointers provided by boost/TR1/C++0x. It is also trivial to implement a scoped pointer if one is not provided by the standard libraries. This sort of reimplementing the wheel only leads to bloating the size of Qt without providing any real value.
I also had a quick look at the documentation for QScopedPointer and was a little perplexed by the comment in QScopedPointer::reset() - “If other is equal to the value returned by data(), behavior is undefined.” It is trivial to protect against this sort of thing, changing “delete m_pointer” to “if (other != m_pointer) delete m_pointer” would resolve the undefined behaviour.
There are several reasons why Qt can’t use TR1 and C++0x. Actually, there’s one. And it should be very obvious, so I don’t have to spell it out for you.
As for using boost, there are two problems with it: 1) horrible API and 2) it would be a new dependency. We really don’t want to add dependency for something so trivial as a scoped/automatic pointer into a third-party C++ library.
Finally, QScopedPointer is completely inline. If you don’t use it, it doesn’t add anything. (Using boost *would* add)
> boost […] horrible API
Totally agree with that!
But isn’t auto_ptr and auto_array a part of stdlib since time immemorial (no boost needed)?
>> boost […] horrible API
>Totally agree with that!
Used to agree with that, but then learned boost.
now I totally disagree and thing that you are a bit ignorant for making such a comment.
@Ville: auto_ptr and auto_array were not good enough for us since we needed custom deleters (see for example QBrush and friends)
Good addition and good arguments from the Qt guys! Personally, I appreciate to see this kind of small utility class added to Qt; espescially it’s more elegant than most other stuff.
All I can say is that it makes me sad to see how you take other people’s ideas and work and present them as your own without giving due credit. Mentioning Boost.ScopedPtr in your OP and telling us how yours is different wouldn’t've been too much to ask for, would it?
@Marc: apologies. Yes, QScopedPointer was inspired on boost::scoped_ptr. Yes, we should have mentioned it.
But I guess that Harald meant no ill-intent. He just forgot because that was not the point of his blog. He simply wanted to show how to use QScopedPointer.
I agree with boostRules_ above. When you understand boost, you will realize the API is quite easy to understand. Talking about horrible API’s, let’s not forget Qt regards its Java-style iterators superior to the C++ styled ones. This coming from guys writing a C++ library. And then we wonder why Java, C#, Eiffel….etc adherents regard C++ as too complex to use. Qt, like Poco & Pt (Platinum C++) should take a leaf & begin using the C++ std library. This makes code more potable & easier to understand by other C++ programmers.
As for now, I appreciate the new QscopedPointer class, & will use it to integrate better with Qt, but boost smart pointers are the Don Vito Corleaone of the C++ smart pointers here ![]()