C++11 standard and Qt5 default GUI
-
Whenever I read about pointers on forums and blogs, it really seems you shouldn't use raw pointers but smart pointers instead. From my perspective, I tend to use
vector
s,list
s and the Qt equivalents such asQVector
andQList
. I am studying howQSharedPointer
s work too. However, I can't help noticing that the default GUI application created in Qt Creator has a default User Interface, and that user interface is dynamically allocated usingnew
and deleted in the main window destructor usingdelete
.
Now, my simple question is: why? Shouldn't you use something likeQUniquePointer
orQSharedPointer
, which smoothly take care of deletions? -
Hi
In a perfect work they should use a smart pointer but please keep in mind that Qt has to work across
many platforms and not all compilers on those platform did support c++11 etc. so
I guess they used the easy way.
From 5.6 they will start to use new language features so it might change later on.Also, before you use smart pointers all over Qt. It has a ownership system where parent deletes children
so often a smart pointer is not needed.For your own data, yes, plesae use all the new smart pointers. Makes more robust code by far.
-
Thank you for answering, @mrjj. I am well aware of the Qt mechanism in which all objects get deleted when their parents get deleted (I find this feature amazing).
Also, in many applications I use
QThreads
and Worker threads. However, a worker thread can't have a parent, otherwise you can't move it to another thread. And the thread itself has no parent. Many tutorials (even the official ones) make use ofdeleteLater()
function.
I have code like this:thread = new QThread; rrtcomputer = new RRTComputer(...initialization parameters...); rrtcomputer->moveToThread(thread); connect(thread, &QThread::started, rrtcomputer, &RRTComputer::start); connect(rrtcomputer, &RRTComputer::newData, &processingInterface, &ProcessingInterface::processEdge); connect(&processingInterface, &ProcessingInterface::updatedData, this, &GraphicalUserInterfaceWindow::updateGUI); connect(rrtcomputer, &RRTComputer::newPath, &processingInterface, &ProcessingInterface::processPath); connect(rrtcomputer, &RRTComputer::finished, &processingInterface, &ProcessingInterface::lastUpdate); connect(rrtcomputer, &RRTComputer::finished, thread, &QThread::quit, Qt::DirectConnection); thread->start();
Since this code is in the mainWindow GUI, should user close the window, worker thread gets stopped and deleted in the destructor like this:
rrtcomputer->requestStop(); thread->wait(); delete thread; delete rrtcomputer; delete ui;
Shall I use
QUniquePointer
for boththread
andrrtcomputer
instead of a raw pointer? -
well you could if they are members of Main or some other object that has long life.
Then you are sure they are deleted with the it.Where do you have doc for QUniquePointer ? I cannot find it.
Im using c++ unique_ptr so want to see the difference.
Also I wonder if u ment QScopedPointer -
@mrjj Sorry, I meant
std::unique_ptr
and I thought there was the equivalent in Qt with the same name.
If I use aQScopedPointer
for a member that is global in a class (i.e. it is defined in the.h
file), does it get deleted when the class destructor is called?
Another thing: if I have a method that dybnamically allocate an object and return that pointer, how shall I use it withQScopedPointer
? Or shall I useQSharedPointer
instead?
For example:QScopedPointer<MyObject> pointerToMyObject = allocatePointer();
being
allocatePointer()
like:QScopedPointer<MyObject> allocatePointer() { QScopedPointer<MyObject> pointer(new MyObject()); // some processing on 'pointer' return pointer; }
But I do think at the end of the function
pointer
will be deleted since it gets out of scope. Shall I use a QSharedPointer instead? -
hi
yes QScopedPointer is for deleting
allocated object when QScopedPointer goes out of scope.
Like in a function where there can be multiple exit location for handling errors or exceptions.Its not so good for returning from a function as it goes out of scope and get deleted.
In such cases std::unique_ptr is better - as its more about who owns the object than auto clean up.
This was a good read (in my opinion)
https://www.safaribooksonline.com/library/view/effective-modern-c/9781491908419/ch04.html -
I think these sorts of questions are best answered when you inspect the reasons for some of the guidelines out there.
For starters: why are smart pointers so widely recommended these days? Well, mostly because of this code:Stuff* foo = new Stuff; foo->doSomething(); //if this throws an exception you've got a leak delete foo;
To avoid this we're recommending this instead:
SomeSmartPointer foo(new Stuff); foo->doSomething(); //ok to throw
So what's inside SomeSmartPointer? To simplify, this:
template<typename T> struct SomeSmartPointer { T* foo; SomeSmartPointer(T* ptr) : foo(ptr) {} ~SomeSmartPointer() { delete foo; } };
Now lets change some names:
struct MainWindow { Ui::MainWindow* ui; MainWindow() : ui(new Ui::MainWindow) {} ~MainWindow() { delete ui; } }; MainWindow mw; mw.doStuff(); //ok to throw, ui will be cleaned up anyway
Huh, how about that? Yes, MainWindow is now a smart pointer of sorts. There's no need to use another smart pointer like there's no need to use
std::unique_ptr<std::unique_ptr<Stuff>>
. One is enough. Basically anything that allocates resources, takes "ownership" of it and cleans them up in the destructor is sorta smart pointer. There's no risk in case of an exception because the destructor will clean up. Unless of course the destructor throws, in which case the problem is really the throwing destructor, not usage of any smart pointers.Some might say: "but but but what if I forget to call delete in the destructor?". Sigh... Then you have a bug. Simple. But ok, lets just pretend for a while that smart pointers are free ride and there's no cost attached. Fine. There's nothing stopping you from using a smart pointer:
struct MainWindow { std::unique_ptr<Ui::MainWindow> ui; MainWindow() : ui(new Ui::MainWindow) {} };
That's fine. You won't forget your
delete
now. One (possibly minor) problem is that you now have to include a header for that smart pointer in your header. It might not seem a biggie, but trust me - these sorts of things become problems 10 years later when your project has grown, and I've seen my share of these.Ok, but since you're using Qt anyway you might want to use a a Qt's smart pointer. Fine. QScopedPointer destroys its pointee when it is destroyed. The scope of a member is the class that hosts it, so yeah, you can use QScopedPointer just the same:
struct MainWindow { QScopedPointer<Ui::MainWindow> ui; MainWindow() : ui(new Ui::MainWindow) {} };
Still fine. A side note here - despite the name QScopedPointer it can just as well be used witout any scoping, e.g.:
QScopedPointer<Foo>* ptr = new QScopedPtr<Foo>(new Foo); delete ptr; //we're still in scope but neither the pointer nor the pointee exist
But I digress.
No what about this other case - a pointer factory function. Qt does not really use much of these. True, it returns a lot of pointers (like
QMenu->addAction()
), but it usually retains ownership of these pointers and just hands out a non-owning raw pointer. There's nothing wrong with that and it's still the recommendation these days, as it's the cheapest way to do this.If you shift the question to your own, non Qt factory functions, then yes - the semantic way of doing this is with smart pointers.
Use std::unique_ptr for passing ownership or std::shared_ptr/QSharedPointer for sharing it. Note that there's no real counterpart of std::unique_ptr in Qt. QScopedPointer is not it. Don't use it for that. Why? Because it has no move semantics like std::unique_ptr does.When you return a std::unique_ptr from a function the result is moved into the std::unique_ptr in the outer scope (well, actually RVO kicks in, but lets just pretend for a moment that compilers are really dumb :) ). QScopedPointer has no move or copy constructor, so this will compile and work fine:
std::unique_ptr<Foo> makeMeAFoo() { return std::make_unique<Foo>(); } std::unique_ptr<Foo> foo_ptr = makeMeAFoo();
while this won't compile:
QScopedPointer<Foo> makeMeAFoo() { return QScopedPointer<Foo>(new Foo()); } QScopedPointer<Foo> foo_ptr = makeMeAFoo();
You might be tempted to use QSharedPointer in this case and it would work, but remember that QSharedPointer, just like std::shared_ptr, is a lot heavier construct, as sharing in a multi-threaded world is a "far from 0" overhead thing. So the point is: don't use QSharedPointer just because it's smart. Use it only when you want to actually share something. A
ui
pointer in a widget class is not one of those things. -
The advice to not use raw pointers is an attempt to say "make sure about ownership, do not leak resources". I.e. the full (proper) advice would be something like "don't use raw pointers unless there's something else taking care of ownership". This "something else" does not exist in plain C++, so people get lazy and drop the "unless ..." part (and other people read the abbreviated form, don't understand where it comes from, and develop an irrational fear of plain pointers).
In a Qt context, QObject parents take ownership of their children, so ownership is handled for all non-toplevel QObjects, i.e. the unqualified advice is plainly wrong. For the remaining few toplevel QObject items it is mostly a matter of taste whether they are put into a QScopedPointer (takes an extra #include + produces a template instantiation) or are deleted manually (needs typing the delete). Qt Creator's choice minimizes the impact of #includes. If that's not of importance, second best choice would be to move the #include "ui_....h" from the .cpp to the .h and make the Ui::Class a proper class member, not a pointer. Third choice would be QScopedPointer or std::unique_ptr.
-
Also, in many applications I use QThreads and Worker threads. However, a worker thread can't have a parent, otherwise you can't move it to another thread.
That's not how it works.
QThread
is aQObject
that manages a thread not the thread itself, it can have a parent. Threads are oblivious to such things as objects or object relationships.A thread has an entry point (a function much like
main()
) and a stack, nothing more, nothing less. Any object you create in the thread's stack is managed by the thread itself, stacks are not shared between the running threads. This doesn't mean that a thread can't modify another thread's stack, it only means that the stack's push and pop (creation and destruction of variables/objects) are done in one thread alone (the one that owns the stack).Anything you create in the heap is shared between all the threads in the process' address space, and is managed by no thread - you manage the heap with
new
anddelete
; there's no automatic creation/destruction of objects there. The heap is just that - a big pile of memory blocks (whence the name).When you call
QThread::start
it invokes the OS' API which spawns a new thread and in the "thread main"QThread::run
is called.QThread::run
by default callsQThread::exec
to start the (thread) event loop. TheQThread
object itself in fact is just happily sitting in either the stack of another thread (if you created it on the stack), or in the heap. It doesn't much care that one of it's functions is called from another thread. So this is the obvious reason why you need to synchronize your threads manually (with mutexes and semaphores) if you reimplementQThread::run
.Shall I use QUniquePointer for both thread and rrtcomputer instead of a raw pointer?
Nope, you should connect the appropriate signals and slots (and not delete explicitly an object that "lives" in another thread):
connect(thread, &QThread::finished, rrtcomputer, &RRTComputer::deleteLater);
On a related note, this:
connect(rrtcomputer, &RRTComputer::finished, thread, &QThread::quit, Qt::DirectConnection);
shouldn't be connected directly. Just use the default parameter (
Qt::AutoConnection
).That's for threads. It's an extensive and somewhat complicated topic. Now back to your original question:
There are quite a lot of pointer wrapper classes in Qt and each has it's own purpose. It's generally not advised to just useQSharedPointer
for everything.Now, my simple question is: why? Shouldn't you use something like QUniquePointer or QSharedPointer, which smoothly take care of deletions?
It should use (if one of the wrappers is preferred) a
QScopedPointer
, which is a thin wrapper around raw pointers and makes sure the managed object is deleted when the managingQScopedPointer
goes out of scope. However, since the form object (I believe that's what you're referring to) is created only in the constructor and freed only in the destructor (i.e. it persists through the whole lifetime of the containing object) it can be easily managed without the wrapper as well, there's no need for the overhead.A note:
There's noQUniquePointer
class.In a perfect work they should use a smart pointer but please keep in mind that Qt has to work across
many platforms and not all compilers on those platform did support c++11 etc.It shouldn't, and this has nothing to do with C++11 (see above).
Kind regards.
-
@Chris-Kawa said:
why are smart pointers so widely recommended these days?
I believe you are right, but important part of why you are right is missing.
Smart pointers in Qt existed way before C++11 and they would be used if it had sense.
It does not only partially because QObject child is deleted by parent.
Main case when smartpointers shine - simplification when providing exception safety.Qt does not guarantee exception safety and there is no way to recover from exception
if it is thrown within Qt code.The code below only look like safe
MainWindow mw;
mw.doStuff(); //ok
In reality it is mostly likely is not.
For example if it uses Qt creator and ui generated code below.class Ui_UCModelerClass
{
public:
QDockWidget *dockWidget_Floors;
QWidget *dockWidgetContents_Floors;
...
void setupUi(QMainWindow *UCModelerClass)
{
dockWidget_Floors = new QDockWidget(UCModelerClass);
dockWidgetContents_Floors = new QWidget();
}
}Above code could be modified to be exception safe manually,
but it can't be done to Qt internals, so
you can only hope that exception would not be thrown
within Qt classes.
If it is, the best you can do - exit.Detailed notes can be found at http://doc.qt.io/qt-5/exceptionsafety.html
-
@alex_malyu
The original post is some 2 months old. So I wrote a whole lot of a post in reply to the original question ... well didn't my ears burn, when I noticed the post time ... I felt like a complete idiot. :)