为什么官方的 Qt 示例和教程不使用智能指针?

为什么有关 Qt 库的官方示例和教程从不使用智能指针?我只看到用于创建和销毁小部件的 newdelete

我搜索了一下原理,但是没有找到,我自己也没有找到,除非是出于历史原因或者向下兼容: 不是每个人都希望程序在小部件构造函数失败时终止,而且通过 try/catch 块处理它非常难看(即使只在少数地方使用)。父窗口小部件可能拥有子窗口小部件的所有权这一事实也只是部分地向我解释了这个问题,因为您仍然必须在某种程度上为父窗口小部件使用 delete

17962 次浏览

You already answered your own question : except if it's for historic reasons/backward compatibility. A library that's as huge as QT can't assume that everyone using the library has compilers that support C++11. new and delete are guaranteed to exist in earlier standards.

However, if you do have the support to use smart pointers I would encourage to use them over raw pointers.

In addition to what @Jamey said:

If you design it cleverly, you may never have to use a delete on a widget. Lets say you have a main window, and you are creating an auto object of it and running that window in event loop. Now rest all of the items in this widget can be added as its children. And since you are adding them to this MainWindow directly/indirectly as child, when you will close this main window everything will be taken care automatically. Just you have to ensure that all the dynamic objects/widgets you have created are children/grandchildren of the MainWindow. Hence no need of a explicit delete..

Because Qt relies on a parent-child model to manage Qobject resources. It follows the composite + Chain-of-responsibility pattern, which is used from event management to memory management, drawing, file handling, etc...

Actually, trying to use a QObject in a shared\unique pointer is overengineering (99% of the time).

  1. You have to supply a custom deleter which will call deleteLater
  2. Your qobject with parents already have a reference in the parent object. So you know that a object is not leaked as long as the parent exist. When you need to get rid of it, you can call deleteLater directly.
  3. Your QWidget without parent already have a reference in the Qapplication object. So same as point 2.

That said, you can still use RAII with Qt. For instance QPointer behaves as a weak reference on a QObject. I would use QPointer<QWidget> rather than QWidget*.

note: to not sound too fanboy, two words : Qt + valgrind.

Smart pointers to children

The smart pointer classes std::unique_ptr and std::shared_ptr are for memory management. Having such a smart pointer means, that you own the pointer. However, when creating a QObject or a derived type with a QObject parent, the ownership (the responsibility to clean up) is handed over to the parent QObject. In that case, the standard library smart pointers are unnecessary, or even dangerous, since they can potentially cause a double deletion. Yikes!

Raw pointers to orphans

However, when a QObject (or derived type) is created on the heap without a parent QObject things are very different. In that case you should not just hold a raw pointer, but a smart pointer, preferably a std::unique_ptr to the object. That way you gain resource safety. If you later hand the object ownership to a parent QObject you can use std::unique_ptr<T>::release(), like so:

auto obj = std::make_unique<MyObject>();
// ... do some stuff that might throw ...
QObject parentObject;
obj->setParent( &parentObject );
obj.release();

If the stuff you do before giving your orphan a parent throws an exception, then you would have a memory leak, if you used raw pointer to hold the object. But the code above is save against such a leak.

On a more general note

It is not modern C++ advice to avoid raw pointers all together, but to avoid owning raw pointers. I might add another modern C++ advice: Don't use smart pointers for objects that are owned by some other program entity.

  • QObject has a parent defined and the tree like structure of the program permits to manage memory quite effectively.

  • Dynamism in Qt breaks that nice ideal, e.g. passing a raw pointer around. One can easily end-up holding a dangling pointer , but that's a common issue in programming.

  • The Qt smart pointer , actually a weak reference, is QPointer<T>
    and provides some of the STL candy.

  • One can also mix with std::unique_ptr and the like , but it should be used only for non-Qt machinery in your program.