For so long i've been programming with the old C++, as me and my team never decided to upgrade to modern programming practices (i admit, part of it was my fault too), so lately i have been studying C++11, C++14, C++17 so i can get the hang of it, and the first thing i came across was std::unique_ptr, which in my opinion is amazing to use, but im still confused about using it with Qt, because i've read that in Qt if i create a QObject thats a child of another QObject, if the parent is removed, than the child will be removed as well, and that using std::unique_ptr might cause double deletion. So i was wondering, is this correct:
MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSqlQueryModel>
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;
QSqlDatabase p_AppDB;
QSqlQueryModel *p_QueryModel;
};
#endif // MAINWINDOW_H
MainWindow.cpp:
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "Dialog.h"
#include <QDebug>
#include <QSqlQuery>
#include <QSqlError>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
p_AppDB = QSqlDatabase::addDatabase("QSQLITE" , "test"); // Set Database connection driver + name
p_AppDB.setDatabaseName("AppDB.db"); // Set SQLite database file name
if(!p_AppDB.open()) // Open database and check if connection failed
{
qDebug() << "ERR: " << p_AppDB.lastError().text(); // Print out an error message
}
p_QueryModel = new QSqlQueryModel(this); // How do i avoid using 'new' here ?
QSqlQuery _Query(QSqlDatabase::database("test")); // Create a new QSqlQuery
_Query.prepare("SELECT * FROM Users"); // Prepare a simple query to select everything from the table 'user'
if(!_Query.exec()) // Execute query and check if the execution failed
{
qDebug() << _Query.lastError().text(); // Print out an error message
return; // Return if the execution failed
}
p_QueryModel->setQuery(_Query); // Set the QSqlQuery with its data to the QSqlQueryModel we created
ui->View_TableView->setModel(p_QueryModel); // Set the QSqlQueryModel with its data to the TableView
// TEST
auto dlg = std::make_unique<Dialog>(); // Create a new Dialog
dlg->exec(); // Execute (Display) the dialog
}
MainWindow::~MainWindow()
{
p_AppDB.close(); // Close the database connection
delete ui;
}
How do i use std::unique_ptr when creating a QWidget for example or anything else instead of using the old: QWidget *w = new QWidget(this);
Im aware that there could be some errors because i havent programmed in quite a while and im now getting back to C++ and Qt again, but i hope if there are any other mistakes that you can point them out.
Thank you
The short answer is that you can't -- the QWidget parent/child ownership scheme and smart-pointers aren't interoperable. You are correct that trying to control them with smart-pointers will often lead to double-delete problems.
In some cases you can do something like std::unique_ptr<QWidget> ptr(new QWidget);, and the QWidget object will get deleted when the unique_ptr goes out of scope, as expected -- but a lot of the functionality in Qt is based on traversing the object-tree that is assembled when you make various QWidget's to be children of other QWidget's, so managing a widget via a smart-pointer is only practical if that widget will never need to be a child of any other widget.
So: When in Qt-land, do as the Qt API does, and use the traditional parent-child-ownership approach where appropriate.
You should not use pass pointers owned by a unique_ptr directly into Qt.
I.e. this would be dangerous:
std::unique_ptr<QLabel> label = std::make_unique<QLabel>();
layout->addWidget(label.get());
addWidget will pass ownership of your pointer to the Qt layout and you will get double deletes as unique_ptr::get() does not relinquish ownership. The same goes for adding widgets to other widgets and the like. Qt assumes that it is taking ownership.
However, it may make sense to use unique_ptr temporarily to avoid leaking inside your method. I.e:
std::unique_ptr<QLabel> label = std::make_unique<QLabel>();
layout->addWidget(label.release());
The difference is the use of release() instead of get(). This will cause unique_ptr to relinquish ownership which in turn will be taken over by the Qt layout and you avoid a naked new in your application code, which could cause a leak if you somehow forget to assign it to a layout or a widget.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With