带有父级的 QSqlQueryModel - 应用程序崩溃

Posted

技术标签:

【中文标题】带有父级的 QSqlQueryModel - 应用程序崩溃【英文标题】:QSqlQueryModel with a parent - app crash 【发布时间】:2014-10-28 21:14:22 【问题描述】:

我对 Qt 比较陌生,也许这就是为什么我不能完全理解子父概念的原因。我需要执行一些 sql 查询。我设置了 QSqlQuery,执行“准备和绑定”操作并执行它。接下来我将它传递给模型并显示数据。关闭窗口时出现问题 - 我收到内存冲突错误。仅当我使用父级创建模型时才会发生错误。代码如下:

QSqlQuery query;
query.prepare(QString("SELECT \
        %1 as nazwa \
        , kontrahentid \
        FROM kontrahent WHERE %2 ilike ?"
    ).arg(showWhat, searchBy) //handled above, no need to escape
);
query.addBindValue(searchString); //user input data - so bind it

if (!query.exec()) 
    qDebug() << query.lastError();
    QApplication::restoreOverrideCursor();
    return;


if (model == NULL)
//  model = new QSqlQueryModel; // app closes the window correctly
    model = new QSqlQueryModel(this); // app crashes when closing the window

model->setQuery(query);
if (model->lastError().isValid()) 
    qDebug() << model->lastError();
    QApplication::restoreOverrideCursor();
    return;


model->setHeaderData(0, Qt::Horizontal, "ID");
ui.kontrahenciList->setModel(model);
//ui.kontrahenciList->setModelColumn(1);
ui.kontrahenciList->show();

这是我得到的错误:

Unhandled exception at 0x0fe29f9a (qsqlpsqld.dll) in HurBudClientGUI.exe: 0xC0000005: Access violation reading location 0x00000004.

和调用堆栈:

qsqlpsqld.dll!QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> >::data()  Line 143 + 0x3 bytes C++
qsqlpsqld.dll!qGetPtrHelper<QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> > >(const QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> > & p)  Line 919 + 0xb bytes  C++
qsqlpsqld.dll!QPSQLDriver::d_func()  Line 106 + 0x13 bytes  C++
qsqlpsqld.dll!QPSQLResultPrivate::privDriver()  Line 212    C++
qsqlpsqld.dll!QPSQLResultPrivate::deallocatePreparedStmt()  Line 306 + 0xc bytes    C++
qsqlpsqld.dll!QPSQLResult::~QPSQLResult()  Line 328 C++
qsqlpsqld.dll!QPSQLResult::`scalar deleting destructor'()  + 0xf bytes  C++
Qt5Sqld.dll!QSqlQueryPrivate::~QSqlQueryPrivate()  Line 94 + 0x23 bytes C++
Qt5Sqld.dll!QSqlQueryPrivate::`scalar deleting destructor'()  + 0xf bytes   C++
Qt5Sqld.dll!QSqlQuery::~QSqlQuery()  Line 245 + 0x1e bytes  C++
Qt5Sqld.dll!QSqlQueryModelPrivate::~QSqlQueryModelPrivate()  Line 90 + 0x3d bytes   C++
Qt5Sqld.dll!QSqlQueryModelPrivate::`scalar deleting destructor'()  + 0xf bytes  C++
Qt5Cored.dll!672cbf06()     
[Frames below may be incorrect and/or missing, no symbols loaded for Qt5Cored.dll]  
Qt5Cored.dll!672cb92a()     
Qt5Cored.dll!672c03f4()     
Qt5Cored.dll!67200dc4()     
Qt5Cored.dll!67203608()     
Qt5Sqld.dll!QSqlQueryModel::~QSqlQueryModel()  Line 175 + 0x9 bytes C++

正如我上面提到的:错误不会发生在(以下之一):

我创建没有父级的 QSqlQueryModel (model = new QSqlQueryModel;) 我将“静态”查询传递给 QSqlQueryModel(无论是否有父级)。

例如:

model->setQuery(
    QSqlQuery(
        QString("SELECT \
            %1 as nazwa \
            , kontrahentid \
            FROM kontrahent"
        ).arg(showWhat)
    )
);

我做错了什么? 真正的问题是:QSqlQueryModel 拥有父级的目的是什么?如果我在窗口的析构函数中手动删除它 - 有什么不同吗?

我猜这是一个错误 - 我在 qt bugtracker 上报告了它: https://bugreports.qt.io/browse/QTBUG-43889

【问题讨论】:

在此处阅读Qts 父/子概念:qt-project.org/doc/qt-4.8/objecttrees.html ... 到您的代码中,您是否删除了析构函数中的模型? 我读过...没有发现任何对我的案子有用的东西。是的,我在析构函数中手动调用delete(如上句所述) 这是你的问题。一旦你把它变成一个孩子,你就不能在任何地方删除它。当父级被释放时,父级会为你删除它。 错误发生得更早——在我在析构函数中添加手动删除之前... 如果是这种情况,您是否为编译器使用了正确的 Qt dll?请记住,您不能使用使用其他版本的 Visual Studio 编译的 Qt dll(这会导致随机堆损坏,因为您将拥有 1 个以上的独立堆)。这就是官方下载包含 1 个以上 Visual Studio 版本的二进制文件的原因。 【参考方案1】:

这是关键部分:

if (model == NULL)
    //  model = new QSqlQueryModel; // app closes the window correctly
    model = new QSqlQueryModel(this); // app crashes when closing the window

Qt 中的父子概念提供了许多特性,自动子销毁就是其中之一。

如果任何QObject 有其他QObject 设置为父对象,则当父对象QObject 被删除时,子对象也将被删除。

现在,您提到您确实在窗口的析构函数中明确删除了model。你不应该。如果你将this定义为父级,那么model会和this一起被自动删除,但是在析构函数中已经被你删除了,所以model被删除了两次,从而出现你的错误。

【讨论】:

【参考方案2】:

所以我删除了析构函数中的“删除操作”代码。错误仍然存​​在,唯一的区别是不同的调用堆栈。 错误信息:

Unhandled exception at 0x0f249f9a (qsqlpsqld.dll) in HurBudClientGUI.exe: 0xC0000005: Access violation reading location 0x00000004.

调用栈:

qsqlpsqld.dll!QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> >::data()  Line 143 + 0x3 bytes C++
qsqlpsqld.dll!qGetPtrHelper<QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> > >(const QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> > & p)  Line 919 + 0xb bytes  C++
qsqlpsqld.dll!QPSQLDriver::d_func()  Line 106 + 0x13 bytes  C++
qsqlpsqld.dll!QPSQLResultPrivate::privDriver()  Line 212    C++
qsqlpsqld.dll!QPSQLResultPrivate::deallocatePreparedStmt()  Line 306 + 0xc bytes    C++
qsqlpsqld.dll!QPSQLResult::~QPSQLResult()  Line 328 C++
qsqlpsqld.dll!QPSQLResult::`scalar deleting destructor'()  + 0xf bytes  C++
Qt5Sqld.dll!QSqlQueryPrivate::~QSqlQueryPrivate()  Line 94 + 0x23 bytes C++
Qt5Sqld.dll!QSqlQueryPrivate::`scalar deleting destructor'()  + 0xf bytes   C++
Qt5Sqld.dll!QSqlQuery::~QSqlQuery()  Line 245 + 0x1e bytes  C++
Qt5Sqld.dll!QSqlQueryModelPrivate::~QSqlQueryModelPrivate()  Line 90 + 0x3d bytes   C++
Qt5Sqld.dll!QSqlQueryModelPrivate::`scalar deleting destructor'()  + 0xf bytes  C++
Qt5Cored.dll!QScopedPointerDeleter<QObjectData>::cleanup(QObjectData * pointer)  Line 62 + 0x20 bytes   C++
Qt5Cored.dll!QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> >::~QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> >()  Line 109 + 0x9 bytes  C++
Qt5Cored.dll!QObject::~QObject()  Line 940 + 0x15 bytes C++
Qt5Cored.dll!QAbstractItemModel::~QAbstractItemModel()  Line 1454 + 0xf bytes   C++
Qt5Cored.dll!QAbstractTableModel::~QAbstractTableModel()  Line 3299 + 0x8 bytes C++
Qt5Sqld.dll!QSqlQueryModel::~QSqlQueryModel()  Line 175 + 0x9 bytes C++
HurBudClientGUI.exe!QSqlQueryModel::`scalar deleting destructor'()  + 0x10 bytes    C++
Qt5Cored.dll!QObjectPrivate::deleteChildren()  Line 1841 + 0x24 bytes   C++
Qt5Widgetsd.dll!QWidget::~QWidget()  Line 1488  C++
Qt5Widgetsd.dll!QDockWidget::~QDockWidget()  Line 1172 + 0x22 bytes C++
HurBudClientGUI.exe!searchDock::~searchDock()  Line 28 + 0x1c bytes C++

searchDock 是我正在使用的类。

【讨论】:

【参考方案3】:

我想我解决了。当模型被初始化为视图的孩子时:

model = new QSqlQueryModel(this);

问题在于操作顺序。当我关闭连接并在我的类的析构函数中删除数据库时 - 数据库断开连接并且无法进行进一步的操作。但是在我的类的析构函数之后,它的基类的析构函数开始起作用——一个接一个,按照继承的顺序。当 QObject::~QObject() 被执行时,它会到达

QObjectPrivate::deleteChildren()

方法。然后,它终于可以删除模型了。该模型想要释放资源 - 这意味着 QSqlResult(在这种情况下具体为 QPSQLResult)。这就是它的外观:

QPSQLResult::~QPSQLResult()

    Q_D(QPSQLResult);
    cleanup();
    if (d->preparedQueriesEnabled && !d->preparedStmtId.isNull())
        d->deallocatePreparedStmt();
;

所以这里 Qt 尝试释放preparedStatement——不管这个连接不再存在的事实:

void QPSQLResultPrivate::deallocatePreparedStmt()

    const QString stmt = QLatin1String("DEALLOCATE ") + preparedStmtId;
    PGresult *result = privDriver()->exec(stmt);
    if (PQresultStatus(result) != PGRES_COMMAND_OK)
        qWarning("Unable to free statement: %s", PQerrorMessage(privDriver()->connection));
    PQclear(result);
    preparedStmtId.clear();
;

所以-为了让它正常工作,我必须打电话给

QSqlQueryModel::~QSqlQueryModel()

QSqlQueryModel::clear()

在关闭与数据库的连接之前。我仍然认为这是一个错误。

【讨论】:

以上是关于带有父级的 QSqlQueryModel - 应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何使具有相同特性的 css 选择器按 HTML 标签父级的顺序应用?

嵌套视图不遵守旋转父级的约束(变换)

JavaFX TabPane 和调整父级的大小

Vue 2 和 NuxtJS - 来自父级的样式子组件

为啥这个 textarea 不能假设它在 Chrome 中的父级的全部高度?

在带有 QSqlQueryModel 的 QListView 上使用 QStyledItemDelegate