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 vectors, lists and the Qt equivalents such as QVector and QList. I am studying how QSharedPointers 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 using new and deleted in the main window destructor using delete.
    Now, my simple question is: why? Shouldn't you use something like QUniquePointer or QSharedPointer, which smoothly take care of deletions?


  • Qt Champions 2016

    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 of deleteLater() 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 both thread and rrtcomputer instead of a raw pointer?


  • Qt Champions 2016

    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 a QScopedPointer 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 with QScopedPointer? Or shall I use QSharedPointer 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?


  • Qt Champions 2016

    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


  • Moderators

    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.


  • Qt Champions 2016

    This post is deleted!


  • @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


  • Qt Champions 2016

    @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. :)



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.