How should I store pointers in Qt?

1.8k views Asked by At

My pet project has come to the point where I should start tracking pointer lifetime, and I'm trying to develop some system for it. Sadly, the popular advice to use smart pointers everywhere does not apply since Qt API itself uses naked pointers on every occasion. So, what I came up with is this:

  1. For everything owned by Qt,

    • use pointers naked locally;
    • pass them between functions naked too;
    • store them as a subclassed QPointer that makes isNull() check before conversion to naked.
  2. For everything cleanly owned by me, use smart pointers as advised. I'm going to go with the std:: versions here.

  3. The case that bothers me. For objects that switch ownership (like widgets added/removed from a layout)

    • use, store, pass pointers naked;
    • delete them manually when appropriate.

Suggestion, comments, advice? I don't like this scheme much myself.

2

There are 2 answers

3
Kuba hasn't forgotten Monica On BEST ANSWER

First, hold things by value where you can. View each use of new, make_unique and make_shared with suspicion - you must justify each dynamic object creation. If a sub-object has the same lifetime as the parent, holding by value is a no-brainer. For example:

class MyWidget : public QWidget {
  Q_OBJECT
  QGridLayout m_topLayout{this};
  QLabel m_sign{"Hello World"};
public:
  MyWidget(QWidget * parent = nullptr) : QWidget{parent} {
    m_topLayout.addWidget(&m_sign, 0, 0);
  }
};

You're passing pointers around, but object ownership is clear and there's no change of ownership. Just because a QObject has a parent doesn't mean that the parent "owns" it. If the child is destructed before the parent, the ownership ceases. By using C++ semantics - namely the well-defined order of member construction and destruction - you have full control over child lifetimes and no QObject parent gets to interfere.

If you have non-movable objects that have one owner, use std::unique_ptr and move it around. That's the way to pass dynamically created QObjects around your own code. You can remove them from the pointer at the point where you make their ownership managed by a QObject parent, if there is such.

If you have objects with shared ownership, where their life should end as soon as possible (vs. when the application terminates, or some long-lived object gets destroyed), use std::shared_ptr. Ensure that the pointer outlives the users. For example:

class MyData : public QAbstractItemModel { /* ... */ };

class UserWindow : public QWidget {
  Q_OBJECT
  std::shared_ptr<MyData> m_data; // guaranteed to outlive the view
  QTreeView m_view;
public:
  void setData(std::shared_ptr<MyData> && data) {
    m_data = std::move(data);
    m_view.setModel(m_data.data());
  }
};

This example is perhaps contrived, since in Qt most users of objects watch the object's destroyed() signal and react to the objects destruction. But this makes sense if e.g. m_view was a third-party C API object handle that had no way of tracking the data object's lifetime.

If the object's ownership is shared across threads, then the use of std::shared_ptr is essential: the destroyed() signal is only usable within a single thread. By the time you get informed about object deletion in another thread, it's too late: the object has been already destroyed.

Thirdly, when you return instances of dynamically created objects from factory methods, you should return them by a naked pointer: it's clear that a factory creates an object for someone else to manage. If you need exception safety, you can return a std::unique_ptr instead.

4
David Haim On

For starter, in Rome, be Roman.
QT was developed in the very early 90's and it was a great success at a time.
unfortunately, QT didn't really adopted new features as the time went by so the API itself has very old C++ style to it (and may I say, a Java style to it?)

you can't force QT to suddenly be C++14 because it's not. use the popular-QT conventions when it comes to QT. use raw pointer if that was the platform design goal. use value types when you can't.

But I don't think you make QT work that much with C++14. stick the QT idioms as they given by the platform.