查询并不意味着是复制的。请改用移动构造

query is not mean to be a copied. Use move construction instead

提问人:Juan Diego Monroy 提问时间:2/16/2023 最后编辑:Juan Diego Monroy 更新时间:2/17/2023 访问量:105

问:

我一直在做一个代码,其中数据库纵,元素被 Qsqlite 数据库和查询保存和编辑,所以我使用的方式是按参数传递查询我不知道它有多糟糕,但每次它通过参数我都会收到这个警告:“ QSqlQuery 已弃用:并不意味着要复制: 改用移动构造“,我想知道正确的方法,我展示了 MainWindow 构造函数(警告在主中指示)。

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{   

    ui->setupUi(this);
    char dirNow[1024];

    db = QSqlDatabase::addDatabase("QSQLITE");
    QString dirfull = QString(_getcwd(dirNow, 1024)) + "\\inventario.db";
    db.setDatabaseName(dirfull);

    if(!db.open()){
        qDebug() << db.lastError().text();
    }

    model = new QSqlQueryModel();

    QSqlQuery query(db);



    if(!query.exec("CREATE TABLE IF NOT EXISTS articulo (codigo INTEGER NOT NULL, nombre VARCHAR(55) NOT NULL, unidades INTEGER NOT NULL, "
                   "categoria VARCHAR(55) NOT NULL, pais VARCHAR(55) NOT NULL, precio DOUBLE NOT NULL, foto VARCHAR(200) NOT NULL, id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT) ")){
            QMessageBox::information(this, "Error", query.lastError().text());
    }

    if(!query.exec("CREATE TABLE IF NOT EXISTS categorias(valor VARCHAR(55) NOT NULL) ")){
        QMessageBox::information(this, "Error", query.lastError().text());
    }

    //query.prepare("DELETE FROM articulo WHERE  = 1");
    //query.addBindValue("");

    "Warning is here: " id = imprimirArticulos(query);

    "Warning is here: " QObject::connect(ui->registrarBtn, &QPushButton::clicked, this, [=]()->void{registrarArticulo(query); });
    QObject::connect(ui->addImagenBtn, &QPushButton::clicked, this, [=]()->void{subirFoto();});
    "Warning is here: " QObject::connect(ui->buscarBtn, &QPushButton::clicked, this, [=]()->void{filtroArticulos(query);});

    "Warning is here: " imprimirCategorias(query);
    "Warning is here: " QObject::connect(ui->categoriasCBox, &QComboBox::currentIndexChanged, this, [=]()->void{agregarCategorias(query);});

    model->setQuery(std::move(query));
}
C++ Qt 警告 qsqlquery

评论

0赞 Sam Varshavchik 2/16/2023
如果您还在显示的代码中指定导致此编译器诊断的确切行,或者如果您让其他人猜测它是哪一行,您认为会更快地得到有用的答案吗?
1赞 Juan Diego Monroy 2/16/2023
这是我的第一个问题,对不起,如果我不知道如何做好。
1赞 Sam Varshavchik 2/16/2023
在 stackoverflow.com 上发布任何问题之前,每个人都应该参加导览,阅读帮助中心,了解最小可重现示例的所有要求以及如何在此处提问。不做任何这些都会导致几乎每次问题的质量都很差。然后它被否决、关闭,然后被删除。重复的低质量问题可能会导致暂时禁止提出新问题。

答:

1赞 user10 2/16/2023 #1

他们只想让对象移动,所以只存在一个查询。QSqlQuery

id = imprimirArticulos(query) //creates copy of query

您需要做的是移动:query

id = imprimirArticulos(std::move(query))

如果您在移动后再次尝试使用,它将是未定义的行为,因此您必须将其从函数移回 main 才能再次使用它。queryimprimirCategorias

评论

0赞 Juan Diego Monroy 2/17/2023
非常感谢你,你的建议帮助了我,我会牢记在心
0赞 Juan Diego Monroy 2/17/2023
对不起,这个问题,但你知道我如何将其返回到主线吗?
0赞 Diego Ferruchelli 2/17/2023
@JuanDiegoMonroy 调用时,将对象置于“未定义行为”状态。除非重新创建对象,否则不应再次调用 move() (或再次直接使用该对象)。但是你不需要这样做:你仍然可以使用(以及传递和返回等)原始调用返回的“xvalue expression”。std::move()queryqueryquerystd::move()
0赞 Diego Ferruchelli 2/17/2023
@JuanDiegoMonroy我刚刚了解了对象的实际副本(触发警告的副本)在您的代码中的位置:在使用它的每个调用中(作为堆栈分配的对象,它是按值传递的)。如果你没有真正改变“原始”对象(例如,在循环中),你有两个选择:忽略警告(这并不重要)或使用 .但是,如果在调用之间更改了“原始”对象,则必须每次创建一个新对象(因为调用将使“原始”对象失效)。querystd::move()std::move()
0赞 Juan Diego Monroy 2/17/2023
是的,我认为处理这个问题的最好方法是忽略警告,好吧,有多个查询对我来说似乎不是最好的事情,感谢您抽出宝贵时间
0赞 Diego Ferruchelli 2/17/2023 #2

扩展 fg 的答案并编辑:当您调用按值传递对象的函数时,编译器会复制它(就像它对任何堆栈分配的对象所做的那样)。像往常一样,复制的确切类型取决于类的复制构造函数。对于这个类,它似乎是一个浅层的副本。因此,当对象具有关联的资源时(例如,当您准备查询并与之关联的绑定变量时),所有实例(原始实例和每个函数使用的副本)都将指向相同的“内部”资源。如果(且仅当)您在 的一个实例中更改某些内容时,这可能会导致问题,从而影响其他实例。queryqueryquery

情况并不完全相同,但您可以在这里阅读:https://doc.qt.io/qt-6/qsqlquery-obsolete.html

QSqlQuery无法有意义地复制。准备好的语句,装订 值等将无法正常工作,具体取决于您的数据库 驱动程序(例如,更改副本将影响原始副本)。 将QSqlQuery视为仅移动类型。

使用 时,可以得到一个 的 “xvalue 表达式”,可以直接传递给某些库重载。这些重载实际上是移动构造函数(并且,因为它们“可能假定参数是对对象的唯一引用”,所以它们很可能会“窃取”对象的内容,从而处于“未定义的行为”状态,调用函数不再可用)。请参见:https://en.cppreference.com/w/cpp/utility/movestd::move(query)queryquery

这里的关键点是:如果对象没有被修改(在你的主代码或被调用的函数中),你可以:a)只是关闭警告并保持代码不变;或 b) 为每个调用使用并创建一个新对象。你不能(你不应该)在调用它后重用它。相反,如果您确实修改了对象(可能在其中一个函数中),则唯一的选择是创建单独的实例。querystd::move()queryquerystd::move()query

评论

0赞 Diego Ferruchelli 2/17/2023
有趣的是,xvalue 表达式移动构造函数可以更改调用者中定义的变量(即变量本身的内容)。这与传递指向对象的引用或指针(以任何方式)并更改其内容不同。std::move()