How much does Qt last release internally and externally use smart pointers?
-
I am totally new to Qt, so please be patient with me : can please somebody explain to me about the question in the subject: how much does Qt last release internally and externally use smart pointers? Above all I am interested to know, as a new Qt framework programmer and user, if I can handle Qt objects with smart pointers (or alternatively local stack storage (what used to be called auto storage)) or if I have to take care of all the uncomfortable new and delete handling. I think I saw in many examples/tutorials the use of the new operator and that rang my mind alarm bell. Thank you so much.
-
Qt is a big library and there's not just one way it manages lifetimes, depending on the type of objects involved. But generally the QObject derived classes are handled via parent/child or object tree model i.e. each object can be assigned a parent and when an object is deleted it deletes all of its children automatically. In that sense a parent is a smart object that handles its children lifetimes.
how much does Qt last release internally and externally use smart pointers?
Well it depends on your definition of smart pointers. If you mean a specific type like std::unique_ptr then almost none in the API. The way Qt is designed there's just no need for it. Internally is a different story, probably quite a lot, but, like I said, Qt is a big library. Different modules have different needs. Qt itself provides a couple of pointer wrappers that you could call smart e.g. QSharedPointer, QWeakPointer, QPointer or QScopedPointer. Each has its own usages, but they are not used as a main way to handle Qt objects lifetimes.
if I can handle Qt objects with smart pointers
Qt is not designed around smart pointers, so although you could use them to some extent (e.g. for root nodes in the object tree) it would lead to more trouble than it's worth. As mentioned above the main object lifetime mechanism in Qt is the parent/child relation.
or if I have to take care of all the uncomfortable new and delete handling
What exactly makes you uncomfortable about them?
I think I saw in many examples/tutorials the use of the new operator
A typical Qt app has a root object created on the stack and then its children added with
new
. When the stack based root goes out of scope it is destroyed along with all its children, so you will rarely see adelete
. A child can be destroyed explicitly by callingdelete
on the pointer, callingdeleteLater()
method, which is usually combined with Qt's signal/slot mechanism, or implicitly by its parent. In any case you have to make effort or completely ignore basic language principles to actually leak something.and that rang my mind alarm bell
Why? It's a perfectly good language construct. Don't let the "oh no, naked new!" fear mongers get to you. The important thing is a solid model of lifetime management and Qt has one. It's just not based on smart pointers.
-
@Chris-Kawa already said it quite well.
I know that with the advent of modern C++ we are told that you should never user a naked
new
. Problem is that Qt predates modern C++. It was long before proper smart pointers were known. Hence, Qt looked for a way around it. Furthermore, object oriented design was quite new and Qt tried to nail the theory perfectly. This all influenced the original design of Qt. Qt has not moved away from this design as it would break backwards compatibility.Everything inside the GUI (now talking about QWidgets, not necessarily QML) if used together with layouts automatically reparents to the containing widget. This takes over ownership of the widgets. Usually, your main window will be on the stack (and thus deleted on exiting the app) and everything else is related to this and thus also deleted. Most of the time dialogs can also be created on the stack. This is why you will (almost) never find a
delete
in proper Qt code.Note that this does not apply to non-GUI related things like containers, strings, etc. If you store pointers inside a container you are back to plain C++ rules (if the pointers are not pointers to GUI widgets or similar).
-
@Chris-Kawa thank you I will study this mechanism parent/child. Could you suggest a link for that? Just to answer your question I hardly ever use new - delete with naked pointers as it's slightly memory leak prone, in general. I prefer to allocate static or on the stack or via shared or unique (smart) pointers as it ensures the RAII principle and (nearly) always clean allocation ( you do not have to worry to ensure always a matching delete for any new) but never mind I will just study the matter.
@SimonSchroeder Thanks yes as I said I need to study this parent/child scheme (a link suggestion would be very appreciated). Yes I am glad you pointed out these "historical" explanations as they better help me to understand and confirm my suspects. Qt indeed seems to be much more than a framework or library, on the C++ side it's like a language extension or language branch ;)
-
@mynickmynick I posted a link in my previous response, but here it is again: Object Trees & Ownership. There's really not that much to it. Each QObject holds a pointer to a parent object and a list of children. Destructor of QObject deletes all of its children.
-
@Chris-Kawa great, thanks, sorry I had not seen the link before
-
-
@Chris-Kawa thanks a lot for your answer, it is really helpful to understand this memory management in qt. But I have two more questions to this topic.
-
What about std::unique_ptr vs QScopedPointer? As I understand, QScopedPointer does not need now and you can use std::unqiue_ptr in each case. It is more about old C++. Am I correct?
For example this description
https://stackoverflow.com/questions/40346393/should-i-use-qscopedpointer-or-stdunique-ptr -
Why do we need explicitly call delete in destructor for .ui class, in qt template for new project? We already have object tree hierarchy and use "this" in ui->setupUi(this). So we connected widtgets with parent. And if I remove delete from destructors, children destructors will still be called.
For example, this qt template, which generate on "new project":
mainwindow.h
QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; };
"mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; }``` ![Screenshot 2023-04-11 113823.png](https://ddgobkiprc33d.cloudfront.net/9b976f4a-29dc-4d07-98af-30086bf7427a.png)
-
-
@zonman said in How much does Qt last release internally and externally use smart pointers?:
Why do we need explicitly call delete in destructor for .ui class, in qt template for new project? We already have object tree hierarchy and use "this" in ui->setupUi(this).
Look at ui_MainWindow.h in the build folder, you will see that Ui::MainWindow class doesn't inherit from QObject, so in setupUi(this) "this" doesn't refer to some parent at all.
-
@mpergand thank you for answer. But our MainWindow inherits from QMainWindow and we use ui->setupUi(this) inside our constructor (this - MainWindow class). So we have Qwidget pointer "this" inside initialize methods in Ui::MainWindow. And all our widgets destructors(from ui file) will be called without "delete ui"
-
@zonman said in How much does Qt last release internally and externally use smart pointers?:
And all our widgets destructors(from ui file) will be called without "delete ui"
delete ui has nothing to do with the MainWindow interface, it only deletes the ui::MainWindow instance, if you don't do that you have a memory leak.
-
@zonman As @mpergand said
MainWindow
andUi::MainWindow
are two different classes.Ui::MainWindow
is a simple "container" class for the widgets set up in the designer. An instance of that class is needed to access them easily. It's not a QObject derived class and does not participate in the parent/child relations.That being said there's no restrictions on how to use that class.
Qt wizard uses pointer approach, but if you're allergic to new/delete you can use a smart pointer, so you don't need to delete it explicitly:class MainWindow : public QMainWindow { ... std::unique_ptr<Ui::MainWindow> ui; }; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(std::make_unique<Ui::MainWindow>()) { ui->setupUi(this); ...
or you can use it as a direct member:
class MainWindow : public QMainWindow { ... Ui::MainWindow ui; }; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { ui.setupUi(this); ...
although not nice thing about that is you need to include the generated header
ui_mainwindow.h
in MainWindow header. I wouldn't recommend it, but it's an option.The
Ui::MainWindow
is just a bag of pointers, so If you do all your setup in the constructor and don't need to access the widgets through theui
variable later on you can even use it entirely locally in the constructor:class MainWindow : public QMainWindow { ... // no ui member at all }; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { Ui::MainWindow().setupUi(this); ...