I recently did a short tongue-in-cheek blog post about Qt and modern C++. In the comments, people discovered that several compilers effectively can optimize std::make_unique<>().release()
to a simple new
statement, which was kind of a surprise to me.
I have recently written a new program from scratch (more about that later), and I tried to force myself to use standard library smartpointers much more than what I normally have been doing.
I ended up trying to apply a set of rules for memory handling to my code base to try to see where it could end.
-
No naked
delete
‘s -
No
new
statements, unless it was handed directly to a Qt function taking ownership of the pointer. (To avoid sillyness like the previous one) - Raw pointers in the code are observer pointers. We can do this in new code, but in older code it is hard to argue that.
It resulted in code like
1 2 3 4 5 6 7 |
m_document = std::make_unique<QTextDocument>(); auto layout = std::make_unique<QHBoxLayout>(); auto textView = std::make_unique<QTextBrowser>(); textView->setReadOnly(true); textView->setDocument(m_document.get()); layout->addWidget(textView.release()); setLayout(layout.release()); |
By it self, it is quite ok to work with, and we get all ownership transfers documented. So maybe we should start code this way.
But there is also a hole in the ownership pass around, but given Qt methods doesn’t throw, it shouldn’t be much of a problem.
More about my new fancy / boring application at a later point.
I still haven’t fully embraced the c++17 thingies. My mental baseline is kind of the compiler in Debian Stable.
I’m a bit skeptical about this code style. It does not really give you any additional compiler checks, while adding a lot of code bloat which reduces readability. I can even easily use the unique pointer after the release(), which is quite plausible when I e.g. want to add a widget to the layout later on etc …
Sure you do not leak a layout you never set on a widget, but that is a bug either way.
I personally think adding STL smart pointers around QtWidgets code simply doesn’t work, unless the library itself is refactored to do that. QtWidgets has a memory management paradigm which is just different from smart pointers.
> So maybe we should start code this way.
No. ;-)
Smart-Pointers everywhere! We tried that too and it caused us a lot of problems. In many places you still need a correctly set parent(), e.g. in QWidgets or QML. And if you’re not very careful, you suddenly have two competing memory managers. This makes everything more complicated and error-prone, not easier. Correctly implemented, the code quickly becomes very ugly. But even experienced colleagues have not always done it right. This led to very difficult to find errors (very often heisenbugs), e.g. those that only occurred in release mode, only after a certain time or only under high load (remember: QML/JS and many other scripting engines have garbage collection).
My conclusions: Don’t do it! If you are developing Qt based libraries then do it the Qt way. If other libraries of your program are not based on Qt, you can have other rules there. In my experience it is easier to follow rules per library/layer than depending on whether the object is used in a certain context or not.
– Hilefoks