在 Qt 上使用 std::unique_ptr

Red*_*edZ 1 c++ qt

长期以来,我一直在使用旧的 C++ 进行编程,因为我和我的团队从未决定升级到现代编程实践(我承认,部分原因也是我的错),所以最近我一直在学习 C++11, C++14、C++17 所以我可以掌握它的窍门,我遇到的第一件事是 std::unique_ptr,在我看来它使用起来很棒,但我仍然对将它与 Qt 一起使用感到困惑,因为我在 Qt 中读到过,如果我创建一个 QObject,它是另一个 QObject 的子级,如果父级被删除,那么子级也将被删除,并且使用 std::unique_ptr 可能会导致双重删除。所以我想知道,这是正确的:

主窗口.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
Run Code Online (Sandbox Code Playgroud)

主窗口.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;
}
Run Code Online (Sandbox Code Playgroud)

例如,在创建 QWidget 或其他任何东西时,我如何使用 std::unique_ptr 而不是使用旧的: QWidget *w = new QWidget(this);

我知道可能会出现一些错误,因为我已经有一段时间没有编程了,现在我又回到了 C++ 和 Qt,但我希望是否还有其他错误可以指出。

谢谢

小智 12

您不应该将 a 拥有的指针unique_ptr直接传递到 Qt 中。

即这会很危险:

std::unique_ptr<QLabel> label = std::make_unique<QLabel>();
layout->addWidget(label.get());
Run Code Online (Sandbox Code Playgroud)

addWidget会将指针的所有权传递给 Qt 布局,并且您将获得双重删除,因为unique_ptr::get()不会放弃所有权。将小部件添加到其他小部件等也是如此。Qt 假设它正在获取所有权。

但是,暂时使用unique_ptr以避免方法内部泄漏可能是有意义的。IE:

std::unique_ptr<QLabel> label = std::make_unique<QLabel>();
layout->addWidget(label.release());
Run Code Online (Sandbox Code Playgroud)

区别在于使用release()代替get()。这将导致unique_ptr放弃所有权,而所有权又将由 Qt 布局接管,并且您可以避免应用程序代码中出现裸露的 new,如果您忘记将其分配给布局或小部件,则可能会导致泄漏。


Jer*_*ner 6

简短的回答是你不能——QWidget 父/子所有权方案和智能指针不能互操作。你是对的,试图用智能指针控制它们通常会导致双重删除问题。

在某些情况下,您可以执行类似的操作std::unique_ptr<QWidget> ptr(new QWidget);,并且QWidgetunique_ptr超出范围时对象将被删除,正如预期的那样 - 但 Qt 中的许多功能都基于遍历在您制作各种QWidget'时组装的对象树s 是 otherQWidget的孩子,所以通过智能指针管理小部件只有在该小部件永远不需要成为任何其他小部件的孩子时才实用。

所以:在 Qt-land 中,像 Qt API 那样做,并在适当的情况下使用传统的父子所有权方法。

  • 是的,这就是我一直在寻找的,谢谢你的回答 (2认同)