0

我正在使用 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 是否会引入某种延迟以允许我的析构函数工作?我该如何解决这个问题?

4

4 回答 4

4

海德的回答是正确的;但是,清除特定哈希的最简单方法如下:

#include <QtAlgorithms>

Foo::~Foo()
{
   qDeleteAll(m_Hash);
   m_Hash.clear();
}

QHash<QString*, QVariant>请注意,如果哈希表的键是指针(例如),则上述技术将不起作用。

于 2012-11-05T16:59:06.453 回答
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 中的大多数数据要么是隐式共享的,要么是很小的。

于 2012-11-05T15:37:45.143 回答
1

文档没有明确提到它,但是你正在改变你正在迭代的容器是一个问题。

代码foreach这里

查看代码,它和您的代码基本上与您编写的代码相同:

for (QHash::iterator it=m_Hash.begin(), end=m_Hash.end(); 
     it!=end;
     ++it)
{
    delete m_Hash.take(key);
}

但是,take成员函数可能会触发现有迭代器 (itend) 的失效,因此您的迭代器可能会变得悬空,从而产生未定义的行为。

可能的解决方案: * 迭代时不要修改你迭代的容器* 确保it在下一次迭代开始之前有效,并且不要存储end-iterator(此解决方案禁止使用foreach

于 2012-11-05T15:28:54.343 回答
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);
于 2012-11-05T15:32:05.923 回答