I recently started investigating Qt for myself and have the following question:
Suppose I have some QTreeWidget* widget. At some moment I want to add some items to it and this is done via the following call:
QList<QTreeWidgetItem*> items;
// Prepare the items
QTreeWidgetItem* item1 = new QTreeWidgetItem(...);
QTreeWidgetItem* item2 = new QTreeWidgetItem(...);
items.append(item1);
items.append(item2);
widget->addTopLevelItems(items);
So far it looks ok, but I don't actually understand who should control the objects' lifetime. I should explain this with an example:
Let's say, another function calls widget->clear();. I don't know what happens beneath this call but I do think that memory allocated for item1 and item2 doesn't get disposed here, because their ownage wasn't actually transfered. And, bang, we have a memory leak.
The question is the following - does Qt have something to offer for this kind of situation? I could use boost::shared_ptr or any other smart pointer and write something like
shared_ptr<QTreeWidgetItem> ptr(new QTreeWidgetItem(...));
items.append(ptr.get());
but I don't know if the Qt itself would try to make explicit delete calls on my pointers (which would be disastrous since I state them as shared_ptr-managed).
How would you solve this problem? Maybe everything is evident and I miss something really simple?
A quick peek into qtreewidget.cpp shows this:
void QTreeWidget::clear()
{
Q_D(QTreeWidget);
selectionModel()->clear();
d->treeModel()->clear();
}
void QTreeModel::clear()
{
SkipSorting skipSorting(this);
for (int i = 0; i < rootItem->childCount(); ++i) {
QTreeWidgetItem *item = rootItem->children.at(i);
item->par = 0;
item->view = 0;
delete item; // <<----- Aha!
}
rootItem->children.clear();
sortPendingTimer.stop();
reset();
}
So it would appear that your call to widget->addTopLevelItems() does indeed cause the QTreeWidget to take ownership of the QTreeWidgetItems. So you shouldn't delete them yourself, or hold them in a shared_ptr, or you'll end up with a double-delete problem.
Qt has its own smart pointers, take a look at http://doc.qt.io/archives/4.6/qsharedpointer.html . Normally, it is though advisable to use the standard ownership hierarchy of Qt whenever possible. That concept is described here:
http://doc.qt.io/archives/4.6/objecttrees.html
For your concrete example, this means that you should pass a pointer to the container (i.e. the QTreeWidget) to the constructor of the child objects. Every QWidget subclass constructor takes a pointer to a QWidget for that purpose. When you pass your child pointer to the container, the container takes over the responsibility to clean up the children. This is how you need to modify your example:
QTreeWidgetItem* item1 = new QTreeWidgetItem(..., widget);
QTreeWidgetItem* item2 = new QTreeWidgetItem(..., widget);
(I don't know what the ... are in your example, but the important thing for Qt memory management is the last argument).
Your example of using a smart pointer
shared_ptr<QTreeWidgetItem> ptr(new QTreeWidgetItem(...));
items.append(ptr.get());
is not a good idea, because you break the golden rule of smart pointers: Never use the raw pointer of a smart pointer managed object directly.
Many Qt class instances can be constructed with a parent QObject*. This parent controls the life time of the constructed child. See if QTreeWidgetItem can be linked to a parent widget, which seems so, reading Qt docs:
Items are usually constructed with a
parent that is either a QTreeWidget
(for top-level items) or a
QTreeWidgetItem (for items on lower
levels of the tree).
Related
My source code ↓
ui->tableWidget->setItem(0,7,new QTableWidgetItem(QString::number(3)));
ui->tableWidget->item(0,7)->setTextAlignment(Qt::AlignCenter);
My approach :
delete ui->tableWidget->item(0,7);
If this memory is free?
If not, let me know any other method.
The call to setItem(...) passes ownership of the QTableWidgetItem to the QTableWidget.
Although QTableWidgetItem is not a QObject, it does take care to inform the QTableWidget about its deletion (from qtablewidget.cpp, Qt 5.1.1):
QTableWidgetItem::~QTableWidgetItem()
{
if (QTableModel *model = (view ? qobject_cast<QTableModel*>(view->model()) : 0))
model->removeItem(this);
view = 0;
delete d;
}
takeItem() sets the view of the item to null, releasing the ownership to the caller.
Because of this, the above code in the item's destructor model->removeItem(this); will not be called.
This means that you need to manually delete the QTableWidgetItem.
But it doesn't matter if you call takeItem(...) or not before deleting the item.
See also Remove QListWidgetItem: QListWidget::takeItem(item) vs delete item.
I recently joined a new project, which is full with idiom like: ,
void foo()
{
Widget* temp = new Widget;
connect(temp, &Widget::signalTriggerred,[this, temp ]()
{
do cool staff...
}
}
As you can see no delete nothing, I am afraid even user class "Widget" is inherited QObject, this is still a leak. Does QT do something fancy to prevent leek in case above?
What I am planning to do:
void foo
{
std::shared_ptr<Widget > temp( new Widget () );
connect(temp.get(), &Widget::signalTriggerred,[this, temp] ()
{
do even cooler things...
}
}
Is there a problem with my apporach? (For example I didn't want to use .get() but compiler errors forced me to use it).
Edit : Since there is no parent in my case it is different. Duplicated question seek answer for parent-child cases. I am already aware in that case there will be no leek. In my question I am asking about creating a local QObject based object. And connecting it.
Not quite sure what you're wanting from the question, but if you need to delete the widget and it is derived from QObject, you can delete it in the lambda expression, assuming it's not going to be used after this scope:
void foo()
{
Widget* temp = new Widget;
connect(temp, &Widget::signalTriggerred,[this, temp ]()
{
temp->deleteLater();
}
}
This very much depends on the context.
If the temp widget is actually a visible top-level widget (no parent QWidget), then the widget needs to be kept alive until the user closes it. You can achieve it getting deleted automatically when being closed using:
widget->setAttribute(Qt::WA_DeleteOnClose);
If the temp widget however is inserted into the layout of some parent widget, Qt will automatically insert it into the QObject ownership tree and temp will have the same lifetime as its parent, see QObject::setParent().
The shared_ptr by itself saves nothing because it does not answer the question of intended lifetime of widget.
The Code
I've been writing this c++ with Qt knowing it works but not really understanding why I sometimes do things other than "I just know I should be doing this".
This is my startup class which initialises my classes:
namespace GUI
{
Startup::Startup() :
QObject(nullptr),
m_setupTab(*new SetupTab(nullptr)),
m_regTab(*new CbcRegistersTab(nullptr)),
m_dataTab(*new DataTestTab(nullptr)),
m_mainView(*new MainView(nullptr,
m_setupTab,
m_regTab,
m_dataTab)),
m_systemController(*new SystemController(nullptr,
Provider::getSettingsAsSingleton())),
m_dataTest(*new DataTest(nullptr,
m_systemController)),
m_setupTabVm(new SetupTabViewManager(this,
m_setupTab,
m_systemController,
Provider::getSettingsAsSingleton() ))
{
}
Then in my header file the member variables are described as such:
private:
SetupTab& m_setupTab;
CbcRegistersTab& m_regTab;
DataTestTab& m_dataTab;
MainView& m_mainView;
Settings* m_settings;
SystemController& m_systemController;
DataTest& m_dataTest;
SetupTabViewManager* m_setupTabVm;
The main difference between the view manager and everything else is that view manager passes signals between the tab classes and everything else.
Then to start this in my main, all I do is this:
GUI::Startup startup;
startup.show();
SetupTabViewManager.cpp:
#include "setuptabviewmanager.h"
#include "View/setuptab.h"
#include "Model/systemcontroller.h"
#include "Model/settings.h"
#include <QDebug>
namespace GUI
{
SetupTabViewManager::SetupTabViewManager(QObject *parent,
SetupTab& tab,
SystemController& sysCtrl,
Settings& config) :
QObject(parent),
m_setupTab(tab),
m_systemController(sysCtrl)
{
WireMessages(config);
WireSetupTabButtons(config);
}
void SetupTabViewManager::WireMessages(Settings& config)
{
connect(&config, SIGNAL(notifyStatusMessage(QString)), //for QT4
&m_setupTab, SLOT(onStatusUpdate(QString)) );
connect(&m_systemController, SIGNAL(notifyStatusMessage(QString)),
&m_setupTab, SLOT(onStatusUpdate(QString)));
}
void SetupTabViewManager::WireSetupTabButtons(Settings& config)
{
connect(&m_setupTab, SIGNAL(onBtnLoadSettingsClicked(bool)),
&config, SLOT(onLoadButtonClicked(bool)) );
connect(&config, SIGNAL(setHwTree(QStandardItemModel*)),
&m_setupTab, SLOT(setHwTreeView(QStandardItemModel*)));
connect(&m_setupTab, SIGNAL(onBtnInitClicked()),
&m_systemController, SLOT(startInitialiseHw()));
connect(&m_setupTab, SIGNAL(onBtnCfgClicked()),
&m_systemController, SLOT(startConfigureHw()));
}
}
Questions
What is the advantage of initialising a class in a member variable as a pointer or a reference? I just know when I make a "view manager", I should initialise the member variable as a pointer. Though I'm not sure why?
Also what is the advantage of "this" or "nullptr" as the parent to the class?
Qt objects are organized in object trees. It allows a programmer not to care about destructing the created objects: they will be deleted automatically when their respective parents are deleted.
If you take a look at almost any Qt GUI application, then you will see that all of its widgets (buttons, checkboxes, panels, etc.) have one single ancestor - the main window. When you delete the main window, all your application widgets are automatically deleted.
You can set up this parent-child relation by passing a parent object to a newly created child:
QWidget *mainWidget = ...
QPushButton *btn = new QPushButton(mainWidget);
In this case, btn object becomes a child of the mainWidget object, and from this moment you may not delete that child manually.
If you create an object without a parent, you should always delete it by yourself.
As thuga mentioned in the comments, you can transfer object ownership later. Widgets can be places inside layouts and it will make them children of the layout owner. Or you can even explicitly set parent with QObject::setParent.
And as for declaring class members as pointers or references - it does not really matter. It's just a question of convenience.
In Qt world all child objects are getting deleted when their parent object is deleting. If you do not define class members (QObjects) as pointers, they will be deleted twice: once by their parent, and second time when your class object is destructed by itself, and this can lead to undefined behavior.
I am using the constructor QWidget(QWidget *parent). This parent widget contains a lot of child widgets. I need to clear all the child widgets from the parent at runtime. How can I do this?
Previous answer is wrong!! You cannot use findChildren to delete a widget's children, because Qt4's findChildren recursively lists children. Therefore, you will delete children of children, which then may be deleted twice, potentially crashing your app.
More generally, in Qt, taking a list of QObject pointers and deleting them one by one is dangerous, as destroying an object may chain-destroy other objects, due to the parent ownership mechanism, or by connecting a destroyed() signal to a deleteLater() slot. Therefore, destroying the first objects in the list may invalidate the next ones.
You need to list children widgets either by:
Passing the Qt::FindDirectChildrenOnly flag to findChild if you are using Qt5 (which did not exist when the question was asked...)
Using QLayout functions for listing items,
Using QObject::children, and for each test if it is a widget using isWidgetType() or a cast
Using findChild() in a loop and delete the result until it returns a null pointer
To take care of the recursivity problem pointed out by #galinette you can just remove the widgets in a while loop
while ( QWidget* w = findChild<QWidget*>() )
delete w;
Summarizing and supplementing:
For Qt5 in one line:
qDeleteAll(parentWidget->findChildren<QWidget*>("", Qt::FindDirectChildrenOnly));
For Qt5 for a lot of children, using setUpdatesEnabled():
parentWidget->setUpdatesEnabled(false);
qDeleteAll(parentWidget->findChildren<QWidget*>("", Qt::FindDirectChildrenOnly));
parentWidget->setUpdatesEnabled(true);
Note that this is not exception safe! While Qt does not at this time appear to throw exceptions here, the signal destroyed() could be connected to code that does throw, or an overridden Object::childEvent(QChildEvent*) could throw.
Better would be to use a helper class:
class UpdatesEnabledHelper
{
QWidget* m_parentWidget;
public:
UpdatesEnabledHelper(QWidget* parentWidget) : m_parentWidget(parentWidget) { parentWidget->setUpdatesEnabled(false); }
~UpdatesEnabledHelper() { m_parentWidget->setUpdatesEnabled(true); }
};
...
UpdatesEnabledHelper helper(parentWidget);
qDeleteAll(parentWidget->findChildren<QWidget*>("", Qt::FindDirectChildrenOnly));
For Qt4:
QList<QWidget*> childWidgets = parentWidget->findChildren<QWidget*>();
foreach(QWidget* widget, childWidgets)
if (widget->parentWidget() == parentWidget)
delete widget;
Removing from the QLayout works in both Qt4 and Qt5:
QLayoutItem* child;
while (NULL != (child = layout->takeAt(0))) // or nullptr instead of NULL
delete child;
QObjects (and therefore QWidgets) remove themselves (automagically) from their parent in their (QObject) destructor.
From Qt docs
The following code fragment shows a safe way to remove all items from a layout:
QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0) {
...
delete child;
}
You can use the following in your parent widget class:
QList<QWidget *> widgets = findChildren<QWidget *>();
foreach(QWidget * widget, widgets)
{
delete widget;
}
I'm very confused about using destructors in Qt4 and hope, you guys can help me.
When I have a method like this (with "Des" is a class):
void Widget::create() {
Des *test = new Des;
test->show();
}
how can I make sure that this widget is going to be deleted after it was closed?
And in class "Des" i have this:
Des::Des()
{
QPushButton *push = new QPushButton("neu");
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(push);
setLayout(layout);
}
where and how do I have to delete *push and *layout? what should be in the destructor Des::~Des() ?
Qt uses what they call object trees and it's a bit different from the typical RAII approach.
The QObject class constructor takes a pointer to a parent QObject. When that parent QObject is destructed, its children will be destroyed as well. This is a pretty prevalent pattern throughout Qt's classes and you'll notice a lot of constructors accept a *parent parameter.
If you look at some of the Qt example programs you'll find that they actually construct most Qt objects on the heap and take advantage of this object tree to handle destruction. I personally found this strategy useful as well, as GUI objects can have peculiar lifetimes.
Qt provides no additional guarantees beyond standard C++ if you're not using QObject or a subclass of QObject (such as QWidget).
In your particular example there's no guarantee that anything gets deleted.
You'll want something like this for Des (assuming Des is a subclass of QWidget):
class Des : public QWidget
{
Q_OBJECT
public:
Des(QWidget* parent)
: QWidget(parent)
{
QPushButton* push = new QPushButton("neu");
QHBoxLayout* layout = new QHBoxLayout(this);
layout->addWidget(push); // this re-parents push so layout
// is the parent of push
setLayout(layout);
}
~Des()
{
// empty, since when Des is destroyed, all its children (in Qt terms)
// will be destroyed as well
}
}
And you'd use class Des like so:
int someFunction()
{
// on the heap
Des* test = new Des(parent); // where parent is a QWidget*
test->show();
...
// test will be destroyed when its parent is destroyed
// or on the stack
Des foo(0);
foo.show();
...
// foo will fall out of scope and get deleted
}
Another option to using deleteLater(), or parents, is to use the delete-on-close functionality for widgets. In this case, Qt will delete the widget when it is done being displayed.
Des *test = new Des;
test->setAttribute( Qt::WA_DeleteOnClose );
test->show();
I like to use it with the object tree that Qt keeps, so that I set delete-on-close for the window, and all widgets in the window have a proper parent specified, so they all get deleted as well.
Richardwb's answer is a good one - but the other approach is to use the deleteLater slot, like so:
Des *test = new Des;
test->show();
connect(test, SIGNAL(closed()), test, SLOT(deleteLater()));
Obviously the closed() signal can be replaced with whatever signal you want.
This tutorial suggests you don't need to explicitly delete widgets that have been added to parent widgets. It also says it doesn't hurt to do delete them either.
(I've not tested this, but I guess as long as you explicitly delete them before the parent widget is deleted, this should be OK.)
In most cases you should create widgets on the stack:
QPushButton push("neu");
This way, they get deleted when they become out of scope. If you really want to create them on the heap, then it's your responsibility to call delete on them when they are not needed anymore.