2010-05-20 41 views
5

那么,我一般对Valgrind和内存泄漏分析器都很陌生。我必须说,当你开始使用它时有点吓人,因为你不能停止想知道你之前可能还有多少漏洞未被解决!是valgrind疯了还是这是一个真正的std映射迭代器内存泄漏?

因为我不是C++程序员的经验,所以我想检查一下是否肯定是内存泄漏还是Valgrind做假阳性?

typedef std::vector<int> Vector; 
typedef std::vector<Vector> VectorVector; 
typedef std::map<std::string, Vector*> MapVector; 
typedef std::pair<std::string, Vector*> PairVector; 
typedef std::map<std::string, Vector*>::iterator IteratorVector; 

VectorVector vv; 
MapVector m1; 
MapVector m2; 

vv.push_back(Vector()); 
m1.insert(PairVector("one", &vv.back())); 

vv.push_back(Vector()); 
m2.insert(PairVector("two", &vv.back())); 

IteratorVector i = m1.find("one"); 
i->second->push_back(10); 
m2.insert(PairVector("one", i->second)); 

m2.clear(); 
m1.clear(); 
vv.clear(); 

这是为什么?不应该clear命令调用每个对象和每个向量的析构函数吗?

现在做一些试验后我发现的不同解决方案的泄漏:

1)删除:

i->second->push_back(10); 

2)添加:

delete i->second; 

3)删除第二

vv.push_back(Vector()); 
m2.insert(PairVector("two", &vv.back())); 

使用解决方案2)使Valgring打印:10分配,11释放可以吗?

因为我不使用新的为什么我应该删除?

谢谢,任何帮助!

+0

不要使用块引号格式化代码,请使用101010图标(或Ctrl + K)。 – 2010-05-20 10:20:41

+0

为编码格式编辑。 – Gorpik 2010-05-20 10:24:28

+1

您对typedefs的使用已使代码难以理解。 – 2010-05-20 10:26:43

回答

1

你不确定的行为在这里:

m1.insert(PairVector("one", &vv.back())); 

vv.push_back(Vector()); 

插入无效迭代器并指向载体,这也意味着你存储在地图基本上是指向插入后,一些黑洞的指针引用。

使Valgring打印:10分配,11释放是否行吗?

这很奇怪,是不是也打印一些关于双释放的东西?

对于解决方案,我建议使用不同于vector的容器(例如listdeque,其变异函数使迭代器失效,但不引用)。或者你可以将指针(最好是智能的,但可能是普通的)存储到向量中的数据,以便实际数据的地址是稳定的。

+0

虽然我不知道如何摆脱调试调用,但我只能看到错误,但它的确提到了无效删除。我将从现在开始使用Valgrind,所以我希望在接下来的日子里能够更加舒适一些...... – 2010-05-20 10:52:09

+2

在你引用的语句中,这不是未定义的行为(尚未定义)。有一个无效的指针悬挂是完全合法的,*只要你不取消引用*。它只在'i-> second-> push_back(10)'这一行中变得未定义,其中向量指针实际上是解除引用的。 – jalf 2010-05-20 11:34:17

0

你在这里正在做一些危险的事情。您正在保存指向程序执行过程中可能失效的向量的指针。

std::vector<>::push_back()可能会使任何迭代器或对std::vector<>的引用无效(如果它已满)。由于std::vector<>保证其内容将被连续存储(所以你可以使用它来代替数组),当它需要更多的内存时,它必须将自己复制到不同的内存块,并且原始内容变得无效。

这意味着在您的代码中所有对push_back()的调用(除了第一个调用)都会导致未定义的行为,因此可能会发生任何事情。

+0

@Gorpik,谢谢你的提示。但告诉我一些事情,我有地图和向量的原因是,向量我保证我的对象在内存中很好地对齐,并且地图用于查找具有特定名称的对象。无意中,这并不打算用于整数,而是大型游戏对象......如果我仔细观察停滞的指针,这对你有意义吗? – 2010-05-20 10:47:49

+0

现在我明白了什么,矢量可以将我的对象内存移动到不同的地方,以便在增长时执行,并且可以让我的地图变得毫无用处。所以我认为我最好使用一个简单的数组。 – 2010-05-20 10:55:26

+0

@Alberto Toglia:不是。如果你确定矢量的大小,你可以在构造中指定它,它永远不会移动。如果你不这样做,那么这个阵列将会过度而不是成长,并且你会在核心上拥有fandango。 – Gorpik 2010-05-20 11:28:52

2

基本上这条线是造成问题:

i->second->push_back(10); 

这是因为I->第二可能你这么做的时候变得无效:

vv.push_back(Vector()); 

第二次。

没有必要呼叫清除。当vv对象超出范围时,它会正确地销毁所有对象。同样所有的地图都不拥有任何矢量,因此它们的析构函数不会影响它们指向的矢量。因此您不需要使用清除。

如果你想保持相同的整体解决方案为你的vv对象创建一个向量列表。然后插入到列表中将不会影响已有的成员,并且您的地图将正常工作。

std::list<Vector> vv; // insertion into this will not invalidate any other members. 
         // Thus any pointers to members you have will not become invalidated. 

就我个人而言,我认为你已经过了复杂的事情。
我认为你可以通过大大简化这个来达到同样的结果。
如果向量没有被多个地图元素引用,那么只需将该向量放入地图。

std::map<std::string, std::vector<int> > m1; 

m1["one"].push_back(10); 
m1["two"].push_back(20);