unique_ptr for RAII?
-
Hi all ! And thank you for reading this.
I wanted to use a kind of RAII class to create database views. The constructor called a function, which created the views, the destructor deleted the views.
Usually I would use a scoped object to get this "RAII" effect - but I want these objects to be member of another class (mainwindow) and the live time of the class should define the existence of the views.
But ... the database is only opened in the constructor of mainwindow - so I "invented" this trick:The member of mainwindow is a unique_ptr<temporaryView>. After the database was opened, I make_shared<temporaryView> the object with the assumption, that at the end of the lifetime of mainwindow the views will be deleted.
This worked fine - on Windows and on Linux in debug. But on Linux / w gcc the opt version removes the make_shared code ... the views are not created.
This leads me to two questions: is there any reason not to use unique_ptr as RAII objects?
And the obvious one: Is it OK that gcc kills my pointer?regards
HoM*member declaration in mainwindow.h:* ... private: std::unique_ptr<tempView> list; *Initialization in mainwindow::mainwindow:* list = make_unique<tempView> (viewname, sql); *constructor of tempView:* tempView(const QString& name, const QString& sql, const QSqlDatabase& db = QSqlDatabase::database()) : name(name) { Q_ASSERT(createView(name, sql, db)); }
-
@HoMa said in unique_ptr for RAII?:
I'm on an ubuntu VM, QtCreator, GCC 64-bit, C++14, Qt 5.15.2. Debug is OK - Release fails :(
@HoMa said in unique_ptr for RAII?:
Q_ASSERT(createView(name, sql, db));
Q_ASSERT
expands to nothing in release, so you shouldn't expectcreateView
to be called at all.@HoMa said in unique_ptr for RAII?:
Why ... well ... it works ;)
What works? Wrapping a
new
in an object, sure it works. Is it necessary? Mostly not. Many things work for many a thing, that doesn't mean it's best, or even good. Why should I do gazillion heap allocations if I don't need to, is a question that often keeps me awake at night ... -
@HoMa said in unique_ptr for RAII?:
But on Linux / w gcc the opt version removes the make_shared code ... the views are not created.
I'm pretty sure this is not correct - gcc will and must not remove this. Please provide a minimal, compilable example for this.
For the rest - why? Not needed for QObject derived classes.
-
Why ... well ... it works ;)
I know, that QObject does lifetime management, but I never got into details and I am not aware, how much overhead comes with making an object an QObject -so I avoided it. I did not need this knowledge so far ;)Here comes the example.
I'm on an ubuntu VM, QtCreator, GCC 64-bit, C++14, Qt 5.15.2. Debug is OK - Release fails :(main.cpp
#include "mainwindow.h" #include <QApplication> #include "executesql.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); prep(); MainWindow w; w.show(); return a.exec(); }
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE #include "executesql.h" struct tempview { tempview(QString n, QString sql) : name(n) { createView(name, sql); } ~tempview() { deleteView(name); }; QString name; }; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; std::unique_ptr<tempview> autoview; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include <memory> #include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); autoview = std::make_unique<tempview> ("myView", "SELECT * FROM t"); } MainWindow::~MainWindow() { delete ui; }
executesql.h
#ifndef EXECUTESQL_H #define EXECUTESQL_H #include <QString> void executeQ(QString sql); void prep(); void createView(QString n, QString vsql); void deleteView(QString n); #endif // EXECUTESQL_H
executesql.cpp
#include <QtSql/QSqlDatabase> #include <QtSql/QSqlQuery> #include <QtSql/QSqlError> #include <QDebug> #include <QFile> void executeQ(QString sql) { QSqlQuery q; if(not q.exec(sql)) { qInfo() << q.lastError(); Q_ASSERT(q.lastQuery().length());; } } void prep() { QString filename {"db.sqlite"}; if( QFile::exists(filename)) QFile::remove(filename); QSqlDatabase db =QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(filename); db.open(); executeQ("CREATE TABLE t (field STRING)"); } void deleteView(QString n) { QString sql {"DROP VIEW %1"}; sql =sql.arg(n); executeQ(sql); } void createView(QString n, QString vsql) { deleteView(n); QString sql {"CREATE VIEW %1 AS %2"}; sql =sql.arg(n, vsql); executeQ(sql); }
-
@HoMa said in unique_ptr for RAII?:
I'm on an ubuntu VM, QtCreator, GCC 64-bit, C++14, Qt 5.15.2. Debug is OK - Release fails :(
@HoMa said in unique_ptr for RAII?:
Q_ASSERT(createView(name, sql, db));
Q_ASSERT
expands to nothing in release, so you shouldn't expectcreateView
to be called at all.@HoMa said in unique_ptr for RAII?:
Why ... well ... it works ;)
What works? Wrapping a
new
in an object, sure it works. Is it necessary? Mostly not. Many things work for many a thing, that doesn't mean it's best, or even good. Why should I do gazillion heap allocations if I don't need to, is a question that often keeps me awake at night ... -
@HoMa said in unique_ptr for RAII?:
QString sql {"CREATE VIEW %1 AS %2"}; sql =sql.arg(n, vsql);
QString sql {"DROP VIEW %1"}; sql =sql.arg(n);
Hello and welcome sql injection...
-
@Christian-Ehrlicher you asked for the minimal code - right?