我正在使用QtCreator和Valgrind检查Qt程序中的内存泄漏。我正在我的析构函数中删除QHash中的一些条目,如下所示:
QHash<QString, QVariant*> m_Hash;
/**
* @brief
* Destruct a Foo Class instance
*/
Foo ::~Foo ()
{
// Do Cleanup here
// Delete hash leftovers
foreach( QString key, m_Hash.keys() )
{
qDebug() << "Deleting an entry..";
// Delete the hash item
delete m_Hash.take(key);
}
}
如果我使用Valgrind调试此代码很好,并在调用析构函数时删除内容:
>> Deleting an entry..
>> Deleting an entry..
如果我在QtCreator中使用GDB启动,从QtCreator启动没有GDB,或者只是从命令行运行我的Qt应用程序,我会收到分段错误!
Signal name :
SIGSEGV
Signal meaning :
Segmentation fault
如果我推荐'删除'行,那么我可以使用任何方法运行我的应用程序,但我确实泄漏了内存。
是什么给出的? valgrind是否会引入某种延迟,使我的析构函数能够工作?我该如何解决这个问题?
答案 0 :(得分:4)
#include <QtAlgorithms>
Foo::~Foo()
{
qDeleteAll(m_Hash);
m_Hash.clear();
}
请注意,如果哈希表的键是指针(例如QHash<QString*, QVariant>
),则上述技术不起作用。
答案 1 :(得分:2)
您无法修改使用foreach迭代的容器。请改用迭代器。使用方法iterator QHash::erase ( iterator pos )
更正代码:
QMap<QString, QVariant* >::iterator it = m_Hash.begin();
// auto it = m_Hash.begin(); // in C++11
while (it != m_Hash.end()) {
delete it.value();
it = m_Hash.erase(it);
}
此外,您存储QVariant指针而不是值的任何特定原因? QVariant通常适合保持值,因为您存储在QVariant中的大多数数据要么是隐式共享,要么是小的。
答案 2 :(得分:1)
documentation没有明确提及它,但是你正在改变你正在迭代的容器是一个问题。
foreach
的代码是here。
查看代码,它和你的代码基本上和你写的一样:
for (QHash::iterator it=m_Hash.begin(), end=m_Hash.end();
it!=end;
++it)
{
delete m_Hash.take(key);
}
但是,take
成员函数可能会触发现有迭代器(it
和end
)的失效,因此您的迭代器可能已经悬空,产生未定义的行为。
可能的解决方案:
*在迭代时,不要修改通过迭代的容器
*确保it
在下一次迭代开始之前有效,并且不存储end
- 迭代器(此解决方案禁止使用foreach
)
答案 3 :(得分:0)
也许foreach关键字存在问题。尝试更换:
foreach( QString key, m_Hash.keys() )
{
qDebug() << "Deleting an entry..";
delete m_Hash.take(key); // take changes the m_Hash object
}
使用:
for (QHash<QString, QVariant*>::iterator it = m_Hash.begin();
it != m_Hash.end(); ++it)
{
qDebug() << "Deleting an entry..";
delete it.value(); // we delete only what it.value() points to, but the
// m_Hash object remains intact.
}
m_Hash.clear();
这样,当您遍历它时,哈希表保持不变。 foreach宏可能会扩展为一个构造,您可以“从脚下删除哈希表”。那就是宏可能会创建一个迭代器,它会变成无效或“悬空”作为调用的副作用
m_Hash.take(key);