Search code examples
c++qtpointerssmart-pointers

Why does Qt use raw pointers?


I have gone back to Qt/C++ programming recently after coding a lot with plain C++.

When browsing StackOverflow, I often catch up on posts like "Why use pointers?" where in most cases the gist of the answers is "if you can avoid it, don't use them".

When coding in C++, I now mostly try using stack variables which are passed by (const) reference or, if necessary, std::shared_ptr resp. std::unique_ptr where needed.

Getting back to Qt, I found all those "principles" to be completely ignored apparently.

I know that Qt uses its own memory management to take care of raw pointers, but here's my question:

Why don't they at least use shared_ptr or unique_ptr, particularly since they even have an own implementation QSharedPointer?


Solution

  • Qt since versions 4.x was designed around imitating Java's framework ideology in C++ environment, using C++98 means. Instead of RAII approach of interaction it establishes "owner" - "slave" relation, in framework's term that's "parent" and "child". More of, Qt uses concept of PIMLP -private implementation. QObjects you operate with aren't real representation of what is happening, they are interfaces to completely hidden inner implementation.

    By design, you have to create a QObject-derived object of child element and pass ownership to the owning object . E.g. where a window is an owner, a Button inside window will be the "slave" object. When owner is deleted, all objects that were slaved to it will be deleted too. All QObjects are thread-aware, but QWidgets can work only in main thread. This creates a non-owning pointer:

    QWidget *myWidget = new QWidget(mainWindow);
    

    mainWindow will be owning QWidget instance in this case. But this one is owning?

    QWidget *myWidget = new QWidget;
    

    It isn't. It's still owned by QApplication.

    QObjectDerivedClass *myWidget = new QObjectDerivedClass;
    

    It's an owning pointer, but this object was registered to exist in our framework. Even more, any instance can be found if it was assigned a name, storing QObjects to reach them is just an caching optimization.

    All QObjects and QWidgets are registered globally and are iterable. With destruction of QApplicationCore instance all QWidgets of top level will be freed. There is undocumented exception out of that rule at least in Qt 4.x versions that QDesktopWidget objects are ignored even if they are top-level widgets. So, if a QMainWindow was forced to appear on certain screen by becoming its child, it wouldn't be destroyed.

    Now comes into play signal-slot connections. In GUI certain handlers begin their work as soon as parent-child connection is established, but you can add new handlers. if a child object is deleted between beginning and end of message pump created by QEventLoop, your program may encounter an UB. To avoid it, you have to call deleteLater() which marks object for deletion at designed moment. Processing signals between threads is done separately. Practically, the main event loop is the only part of GUI that is synced with other threads.

    With such complex structure and already existing requirement of working in one thread, imposed by some of supported embedded platforms, need to use smart pointers within GUi framework was negligible compared to possible impact on performance.

    Before C++11 Qt had QPointer (and still got it), which is aware if QObject still exists or not using mechanics similar to owner-child interaction.

    This design predates C++11 and QSharedPointer appeared only after that to fill a niche requested by users to maintain user-defined data model. It doesn't support some features, e..g you can't have an atomic version of it like ISO version does, it's only partially atomic until very last releases of 5.x. QAtomicPointer isn't either QPointer or QSharedPointer, it acts like a std::atomic<QObject*>. QSharedPointer though allows use of custom non-standalone deleter, including a call of deleteLater():

    QSharedPointer<MyDataQObject> objPtr { new MyDataQObject, &QObject::deleteLater };
    

    Assuming that MyDataQObject is derived from QObject, objPtr will call method deleteLater() in context of managed object instead of using delete on managed pointer when destroyed or reset.