在Qt上使用std::unique_ptr
Using std::unique_ptr on 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
主窗口.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;
}
例如,在创建 QWidget 或其他任何东西时,我如何使用 std::unique_ptr 而不是使用旧的:QWidget *w = new QWidget(this(;
我知道可能会有一些错误,因为我已经有一段时间没有编程了,我现在再次回到C++和Qt,但我希望如果还有其他错误,你可以指出它们。
谢谢
简短的回答是你不能 - QWidget父/子所有权方案和智能指针是不可互操作的。 您是对的,尝试使用智能指针控制它们通常会导致双重删除问题。
在某些情况下,你可以做一些类似std::unique_ptr<QWidget> ptr(new QWidget);
的事情,当unique_ptr
超出范围时,QWidget
对象将被删除,正如预期的那样——但 Qt 中的许多功能都是基于遍历当你将各种QWidget
设置为其他QWidget
的子对象时组装的对象树, 因此,仅当该小部件永远不需要是任何其他小部件的子项时,通过智能指针管理小部件才实用。
所以:在Qt-land时,像Qt API一样做,并在适当的时候使用传统的父子所有权方法。
你不应该使用unique_ptr
拥有的直接传递指针到Qt中。
即这将是危险的:
std::unique_ptr<QLabel> label = std::make_unique<QLabel>();
layout->addWidget(label.get());
addWidget
会将指针的所有权传递给Qt布局,并且您将获得双重删除,因为unique_ptr::get()
不会放弃所有权。将小部件添加到其他小部件等也是如此。Qt假设它正在获得所有权。
但是,暂时使用unique_ptr
以避免在方法内部泄漏可能是有意义的。即:
std::unique_ptr<QLabel> label = std::make_unique<QLabel>();
layout->addWidget(label.release());
不同之处在于使用release()
而不是get()
。这将导致unique_ptr
放弃所有权,而所有权又将被Qt布局接管,并且您可以避免应用程序代码中出现裸露的新内容,如果您以某种方式忘记将其分配给布局或小部件,可能会导致泄漏。
您可以将std::unique_ptr
和std::shared_ptr
与自定义删除器一起使用。
#include <memory>
#include <type_traits>
#include <QObject>
#include <QThread>
namespace qt {
namespace _details {
template<typename T>
struct q_deleter
{
// T is not QThread
template <typename Q = T,
typename std::enable_if<!std::is_base_of<QThread, Q>::value>::type* = nullptr>
void operator()(T *ptr_)
{
static_assert(std::is_base_of<QObject, T>::value,
"stdx::qt::*_ptr<T>: T must be QObject or its children!");
ptr_->deleteLater();
}
// T is QThread
template <typename Q = T,
typename std::enable_if<std::is_base_of<QThread, Q>::value>::type* = nullptr>
void operator()(T *ptr_)
{
static_assert(std::is_base_of<QObject, T>::value,
"stdx::qt::*_ptr<T>: T must be QObject or its children!");
ptr_->quit();
ptr_->deleteLater();
}
}; // struct q_deleter
} // namespace _details
template<typename T, typename D = _details::q_deleter<T>>
using unique_ptr = std::unique_ptr<T, _details::q_deleter<T>>;
template <class T, typename D = _details::q_deleter<T>>
unique_ptr<T> make_unique() {
return unique_ptr<T, D>(new T());
}
template <class T, typename D = _details::q_deleter<T>, class... Ts>
unique_ptr<T> make_unique(Ts&&... args) {
return unique_ptr<T, D>(new T(std::forward<Ts>(args)...));
}
template<typename T>
using shared_ptr = std::shared_ptr<T>;
template <class T, typename D = _details::q_deleter<T>>
shared_ptr<T> make_shared() {
return shared_ptr<T>(new T(), _details::q_deleter<T>());
}
template <class T, typename D = _details::q_deleter<T>, class... Ts>
shared_ptr<T> make_shared(Ts&&... args) {
return shared_ptr<T>(new T(std::forward<Ts>(args)...), _details::q_deleter<T>());
}
} // namespace qt
使用此标头,您可以使用标准库中的智能指针。但是在构造对象时不能传递父指针(nullptr
除外(。 用法示例:
auto qobj_ptr = qt::make_unique<QLabel>();
- 为什么 std::unique 不调用 std::sort?
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 为共享 ptr 向量实现复制 c'tor?
- 字符和整数中 **(ptr+1) 的值差异
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 生成"unique"矩阵
- 如何将派生类从基 ptr 分配给 nlohmann::json
- 引用 std::shared:ptr 以避免引用计数
- 我对 std::unique(算法)C++有问题
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- 尝试打印出 *ptr++ 的值,以了解它是如何工作的
- 如何控制共享 ptr 引用计数?
- std::shared_ptr::unique(),复制和线程安全
- 如何在C++03中用自定义谓词调用std::unique
- C++中的指针否定 (!ptr == NULL)
- C++14 unique_ptr并使用已删除的函数'std::unique-ptr' unique_ptr错误