C++ Programming Practices – Safest Practice in Handling QWidget Pointer Lifespan in QObject Environment

cmemorypointersprogramming practicesqt

Consider the following constructor:

NetworkTools::NetworkTools(QObject *parent) : QObject(parent)
{
    view = new QWebEngineView();
    view->setParent(parent); // Produces an error, shown below.
}

While QWebEngineView is a QWidget, it is able to be utilized without ever having to open a widget window, using:

void QWebEngineView::load(const QUrl &url)

in place of

void QWidget::show()

As such, I will be integrating it into various QObject based console applications. The issue however is setting parents. The code above produces this error:

error: invalid conversion from ‘QObject*’ to ‘QWidget*’ [-fpermissive]
view = new QWebEngineView(parent);
                                ^

According to this answer on SO,

Qt is not designed to support a non-widget parent to a QWidget

but then states later:

It's not a worthwhile fight, I think – not unless a decision is made to make a QWidget truly-a QObject and change its constructor signature. That can be done at the earliest in Qt 6 since it's a binary-incompatible change AFAIK.

It is suggested that casting the parent will just lead to issues, so my solution is just making sure all my QWidget pointers belong to a class, and to simply delete them in the class destructor like so:

NetworkTools::~NetworkTools() {delete view;}

Compromising the setParent functionality. I am not even sure if that is the proper way to delete a QWidget pointer. As such:

  1. Is it true that Qt6 plans on letting QObject serve as a parent to
    QWidget?

  2. Is my destructor method done right, and is it a safe strategy in preventing memory leaks?

  3. Is there a safer practice available?

  4. Should I be utilizing any of Qts smart pointers instead:

Thanks.

Best Answer

1.

Your linked SO answer just talks about the earliest possible point where such a change could occur. Personally I’ve never seen a proposal or discussion about actually doing something like that.

2. to 4.

tl;dr: Sidestep the problem and make the view a plain member of NetworkTools.

Longer answer: Your approach uses RAII, which is the preferred way to handle ownership in C++. That’s perfectly valid, but it’s not ideal in this case.

From your question I can’t see a reason why you should allocate the QWebEngineView outside of NetworkTools at all. It can be a plain member of NetworkTools. It’s tied to the lifetime of NetworkTools anyway and you’d avoid any special work in the ctor or dtor as well as a bunch of pointer indirections when accessing the view member.

You might want to allocate the view separately if it is huge and you want to allocate (lots of) NetworkTools objects on the stack – although that seems unlikely. With huge objects, running out of stack space is a real possibility. However, on my Qt 5.10 64bit Linux sizeof(QWebEngineView) == 56. That’s not super tiny, but nothing to worry about. For comparison: sizeof(QString) == 8 and sizeof(QObject) == 16.

If you really absolutely have to allocate the view separately, prefer std::unique_ptr for the simple reason that it is the default single-ownership smart pointer provided by the C++ standard library itself. Qt’s smart pointers mostly originate in ancient times when C++ didn’t have any smart pointers. There are two situations where I’d consider a Qt smart pointer:

  • If I wanted to tap into Qt’s implicit-sharing/copy-on-write system. That’s what the SharedData pointers are for.
  • To avoid needless conversions if I had to use a part of Qt’s API that requires a Qt smart pointer.

For everything else I’d default to std::unique_ptr or std::shared_ptr for the few special cases where you have real shared ownership.

Related Topic