Some more pointing
August 25, 2009 by Harald Fernengel | Comments
Instead of answering all the comments about QScopedPointer, I decided it's easier to post a follow-up post :) Thanks to Thiago's blog, the differences between the various smart and not so smart pointers should hopefully be clearer now.
Using QScopedPointer instead of std::auto_ptr or boost's scoped pointer has nothing to do with not-invented-here syndrome. Very early during our API discussions we realized that delete-on-end-of-scope was not enough, we also needed custom cleanup handlers. Take QBrush as example - extra magic is required when deleting the private object. The other pointer classes did not offer that functionality, so they were ruled out.
Why did we make the custom deleters a template argument, and not simply a function pointer member in the class? This has two reasons. Firstly, we didn't want any penalty from using QScopedPointer over manual new/delete. Setting an extra pointer, and calling it in the destructor would have introduced an extra overhead. Secondly, due to our binary compatibility policy, the size of QScopedPointer must be sizeof(void*), so adding another member variable was not possible.
Custom cleaners can be very useful - you can basically use QScopedPointer for any cleanup when leaving scope, for example to close a handle, decrease some reference count, call a custom free function and so on.
Still not in love with QScopedPointer? Let's look at the following, then:
class ForwardDeclared;
extern ForwardDeclared *myFunc();...
std::auto_ptr<ForwardDeclared> ptr(myFunc());
It compiles, but you'll get a nasty surprise at runtime - since the class is only forward declared, its destructor will not be called on deletion! QScopedPointer has extra protection, forcing a compilation error in the case above.
To make sure that you can write if (myScopedPointer) ..., we had to introduce an operator bool() - but that would also enable writing int x = myScopedPointer;, due to the implicit boolean to integer conversion. Thankfully, we found that issue during an API review, so the latter case will now not compile.
Still not deeply in love with QScopedPointer? Good news - it's all template code, all inline, so there's no penalty to Qt's library size or runtime behavior if you choose to ignore our little gem :)
Blog Topics:
Comments
Subscribe to our newsletter
Subscribe Newsletter
Try Qt 6.9 Now!
Download the latest release here: www.qt.io/download.
Qt 6.9 is now available, with new features and improvements for application developers and device creators.
We're Hiring
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.
Commenting for this post has ended.
Implement your own, for what ever reasons you like, but be polite and admit that QScopedPointer is not the novelty your original blog entry implied. QScopedPointer might be a nice implementation, but it has nothing we haven't seen before in terms of API. It's basically boost::scopedptr with some nice binary properties and custom deleter functionality, just as we all know it from boost::sharedptr. Also, Thiagos comment bashing boost's API as awful is misplaced with regard to boost::sharedptr and boost::scopedptr which are both straight-forward to use.
@Thomas: So it's just a non-novel thing, it just is novel? :-P
I mean, C++ is really non-novel, it didn't really bring anything we hadn't seen anywhere else.
@thomas. Speak for yourself. This is going to very useful for me to have in Qt directly. Boost does have an awful API, and it's another dependency that's a big pain in the ass to compile on windows. I'll be very glad to toss it in favour of a nicer Qt solution.
If for some reasons you can't use what is implemented in Boost and have to implement your own, I accept it. But saying that you do it because you don't like the API is something else. There is nothing wrong with the Boost API. It's accepted into the C++0x standard. If integration is hard, re-implementing some features is acceptable. I think it's natural and there's no point in bashing the library you take the idea from.
@ Leo: if you don't know how to compile boost, there's the boostrap exe in the boost directory. Just run it and then run bjam, it will build boost for you.
"binary compatibility"
And now please a big blog about why is binary compatibility is so important!
Who uses it? Who requires it? Why is it so bad to break binary compatibility in a Y.X++ (4.5->4.6) change?
boost does not support binary compatibility, and I've never heard that this is a showstopper to use boost.
To always cry "binary compatibility!" looks a little bit manic to me.
@Thomas, boost is not the mother of all. Ideas implemented in boost come from various old sources. In the case of auto ptr, going back up to the dtor concept!
Personally, I certainly don't want to depend on boost, hence I understand Qt's concerns.
And on the hand, well integrated and elegant refinements of known concepts, and very welcome.
I wish the same could be done in certain other parts of Qt...
@everyone disagreeing with me above: I don't mind that Qt implements its own QScopedPointer, I don't even disagree with it, I just found the first QScopedPointer blog entry seriously annoying because it completely failed to mention that QScopedPointer isn't some novel new idea conceived in the Qt offices.
The one thing you've added to your scoped pointer is the one thing I wish Boost's had - custom deletion. Would be extremely useful for GDI handle wrappers in Windows, things like that, where instead, I wrote my own custom class. Still, not too much hassle.
'Boost has an awful API' - can't agree with that. Different to Qt, but not awful'
'Boost ... [is] a big pain in the ass to compile on windows' - really? Can't say I've ever had any problems with it. Just invoke bjam with the correct parameter set (and that's just telling it what toolset to use, where to stick the libraries and to build all variants) and you're done. OR you could use the installer @ BoostPro (http://www.boostpro.com/dow...) if that's too difficult.
Why the fuss? No one is forced to use Qt or boost pointers.
P.S: I would rather have a blog post that announces the new version of Qt Creator than posts about pointers :D
@Razvan => Pointers are fun! :)
I've loved the concept of RAII ever since reading "Effective C++" (Scott Meyers). I was disappointed to find no scoped pointer class in Qt, and I started using shared_ptr from TR1 instead. QScopedPointer sounds like a great improvement. Just having a Qt class to do the job will make the code more consistent. I'm really excited about using it. Great job!
To me, code and API consistency is pretty important. In a Qt app, I'd rather use a Qt class to do RAII. If I were using the STL, Boost would be a better fit. Qt and Boost obviously have very different approaches to their API. It's silly to quarrel over which is better -- they clearly appeal to different people.
Compiling the ForwardDeclarion example (with a few lines to make it a validish program) gives me
/usr/include/c++/4.3/backward/autoptr.h: In destructor 'std::autoptr::~auto_ptr() [with Tp = ForwardDeclared]':
test.cpp:7: instantiated from here
/usr/include/c++/4.3/backward/autoptr.h:173: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
So that bit of scoped_ptr sorry QScopedPointer certainly isn't new :)
(But yes, it is nice to avoid a new dependency on windows.)
And about binary compability: The boost way of doing things is a major hazzle for distributions. It means that you need to have boost-x.1 boost-x.2 and boost-x.3 installed side-by-side with almost identical libraries since application packages needs to depend on one specific version.
BTW: Your blog sucks! :) No openid support, and the captcha uses O and 0!
Ok, I admit it: Qt is no novelty, there have been UI toolkits before.
QScopedPointer is no novelty, there have been smart pointers before. I just got a bit overexcited because we've put a lot of love in QScopedPointer, so in my biased opinion, it's the scoped pointer to rule them all :)
@comment, who said:
> Who uses it? Who requires it? Why is it so bad to break binary compatibility in a Y.X++ (4.5->4.6) change?
Oh, so just because you don't need it, nobody else does? ;-)
Well for my company it is extremely useful. We have many binary packages that we have to build / maintain for four different platforms, and several of them are linked to Qt. It would cost days of extra work every time we upgraded if all of them had to be rebuilt against each new Qt version.
@comment, who said:
> Who uses it? Who requires it? Why is it so bad to break binary compatibility in a Y.X++ (4.5->4.6) change?
I could remember a KDE upgrade (4.2 with Qt 4.4/5?) where all the binary compatibility does not help, because the new version has fixed too much bugs and all the workarounds haven't worked any more.
I assume in no serious Qt product process one blindly relies on the binary compatibility and just do a Y.X++ update by simply replacing the shared Qt libraries, I'm sure this will make to much trouble.
For a Y.X.Z++ upgrade it will be ok, but for a X.Y++ upgrade you need a complete QA cycle, then a new compilation of all relevant parts wouldn't hurt that much.
@comment: you should always do QA. Qt does its best to maintain behaviour compatibility, but some things break. Especially when you're using novel functionality which isn't hardened yet -- or when you're exploiting undocumented features in Qt. KDE does that a lot.
In any case, you're thinking of the Qt 4.5 upgrade and KDE 4.2. Tell me, how many of your projects are the size of KDE?
But the fact that you have to do QA doesn't mean that binary compatibility isn't necessary. For smaller applications, the upgrade is very straight-forward. Without binary compatibility, the upgrade becomes very difficult, if not impossible.
@Thomas: I never bashed Boost API. Quite to the contrary, I said they have lots of cool ideas. I also made a statement of fact that their API style is differnt from Qt's style.
And I stated my opinion that I find their API horrible. I'm allowed to have my opinion.
@harald
Harald, you've made yourself guilty of writing some free code, and made it worse by telling others that you actually like your implementation. I'm not sure what the minimum sentence for such a crime in the so-called community is, I think you'll might get away with just around 16 hate mails or blog comments :-D
@harald: I like your pointer. It's a good pointer. And btw I like it when you can control when memory is released. In GC i don't trust.
I'm with Matthias. No-one force anyone to use Boost or Qt. For me the biggest problem that both Qt and Boost APIs are nice (both, really!) but looks differently. Well, C++ doesn't enforce code style like some other languages, use what you like more.
Harald, Thiago, Matthias, other friendly trolls, boost people - thanks for great code.
This is a sugestion:
You could rename the cleanup handlers QScopedPointerDeleter, QScopedPointerArrayDeleter, QScopedPointerPodDeleter -
QPointerDeleter, QPointerArrayDeleter and QPointerPodDeleter reespectively. This way you could use the cleanup handlers in more than one class, and I would be able to use it in my own classes avoiding the confusion with name QScoped*