问题描述:
存在如下代码:
/// <summary>
/// 创建一个 sqlite链接
/// </summary>
/// <param name="connectionName">链接名称</param>
/// <returns>链接</returns>
inline std::shared_ptr< QSqlDatabase > make_QSqlDatabase( const QString &connectionName ) {
if( QSqlDatabase::contains( connectionName ) )
return std::shared_ptr< QSqlDatabase >( new QSqlDatabase( QSqlDatabase::database( connectionName ) )
, []( QSqlDatabase *p ) {
if( p->isOpen( ) )
p->close( );
delete p;
} );
else
return std::shared_ptr< QSqlDatabase >( new QSqlDatabase( QSqlDatabase::addDatabase( "QSQLITE", connectionName ) )
, []( QSqlDatabase *p ) {
if( p->isOpen( ) )
p->close( );
delete p;
} );
}
已知它能为我们链接一个 “connectionName ”名称的数据库链接。
QSqlDatabase::addDatabase( "QSQLITE", connectionName ) 允许你基于驱动实现一个 sqlite 链接。
QSqlDatabase::database( connectionName ) 允许你重复链接一个已经存在的 “connectionName ”名称的数据库链接
现在,重复调用它。甚至释放
{
auto dbLink = make_QSqlDatabase("234");
dbLink->open(); // true
}
{
auto dbLink = make_QSqlDatabase("234");
dbLink->open(); // false
}
解决方案
使用 "QSqlDatabase::removeDatabase" 删除链接名称。
/// <summary>
/// 创建一个 sqlite链接
/// </summary>
/// <param name="connectionName">链接名称</param>
/// <returns>链接</returns>
inline std::shared_ptr< QSqlDatabase > make_QSqlDatabase( const QString &connectionName ) {
static QMap< QString, size_t > userCount; // 保存链接次数,比要时候对其进行删除
/// 释放处理调用
auto releaseFunction = []( QSqlDatabase *p ) {
QString connectionName = p->connectionName( );
if( p->isOpen( ) ) {
p->commit( );
p->close( );
}
delete p;
auto iterator = userCount.begin( );
auto end = userCount.end( );
for( ; iterator != end; ++iterator )
if( iterator.key( ) == connectionName ) {
size_t &value = iterator.value( );
--value;
if( value == 0 )
QSqlDatabase::removeDatabase( connectionName );
break;
}
};
if( QSqlDatabase::contains( connectionName ) ) {
++userCount[ connectionName ];
return std::shared_ptr< QSqlDatabase >( new QSqlDatabase( QSqlDatabase::database( connectionName ) )
, releaseFunction );
} else {
userCount.insert( connectionName, 1 );
return std::shared_ptr< QSqlDatabase >( new QSqlDatabase( QSqlDatabase::addDatabase( "QSQLITE", connectionName ) )
, releaseFunction );
}
}
使用 QSqlDatabase::removeDatabase( connectionName ); 时,必须在数据库关闭之后,否则它会发生内存泄露!这是官方的提醒。这也说明,一个 QSqlDatabase 的关闭并不影响它被释放。
官方正确案例
{
QSqlDatabase db = QSqlDatabase::database("sales");
QSqlQuery query("SELECT NAME, DOB FROM EMPLOYEES", db);
}
// 在"db" 和 "query" 在堆栈已经释放
QSqlDatabase::removeDatabase("sales"); // 正确的