c ++ std vector - 无效的迭代器问题

时间:2009-09-30 00:07:28

标签: c++ std stdvector

我有一个标准的指针向量。

在什么情况下,此向量中的迭代器可能会失效?

我有理由相信,当一个对象被删除时,任何引用它的向量迭代器都会因此失效。然而,这对我来说似乎不正确。我相信这将是Managed .NET中容器的标准行为,但在c ++中这似乎不合适。

for (It = Vec.begin(); It != Vec.end(); It++){
  GoToOtherCode((*It));
}

function GoToOtherCode (ObjectType* Obj){
  delete Obj;
}

这会使Iterator失效吗?在我看来它不应该,但是我遇到了一个难以调试的问题! (我害怕我的解决方法 - 通过整数索引遍历向量。(这很好......我只是害怕上面导致失效问题的原因)。

提前感谢您的时间。

编辑:感谢您的建议。普遍的共识是上述代码是危险的,但它不会使迭代器无效。我相信我在Visual Studio 2008调试器中遇到错误,因为在第二天打开项目后,这个无效的问题就消失了。所以 - 就像计算机中的许多东西一样,如果其他任何东西看起来都不起作用,请尝试重置该东西。

8 个答案:

答案 0 :(得分:8)

它不会使迭代器失效,它实际上是你删除向量所拥有的堆分配对象的方式,clear()方法不会为你做那个。这很常见:

for (It = Vec.begin(); It != Vec.end(); It++)
  delete *It;

Vec.clear();

如果您不尝试使用刚刚删除的内容,那就非常好。

答案 1 :(得分:3)

它不会使迭代器失效,但指针本身会指向现在删除的对象,所以你真的需要做些什么来确保你的代码不会在现在悬空的指针上绊倒。例如,您可以将指针设置为空值,或使用erase从向量中删除已删除的项目。

答案 2 :(得分:2)

如果我没有弄错,迭代器解除引用*iter会返回引用,因此您可以让函数接收指针引用。然而,这可能不是最佳方式。

for(It i = vec.begin(); i != vec.end(); i++)
{
    GoToOtherCode(*i);
}

void GoToOtherCode (ObjectType *& pref)
{
    // This *should* set the iterator's copy of the pointer to null
    delete pref;
    pref = 0;
}

然后你可以检查向量中的空值。 警告:未经测试的代码。

答案 3 :(得分:1)

这不应该使你的迭代器失效 - 但是......

这里的危险在于,虽然您已删除了ObjectType实例,但您的Vec向量仍包含指向原始内存位置的指针。 Vector不知道该实例已被删除。

矢量本身应该没问题 - 它只会指向许多不再有效的位置。

答案 4 :(得分:1)

可能导致某些奇怪现象(也可能是损坏)的一件事是将指向实例的指针删除到一个类,该类派生自没有virtual析构函数的类。我不知道是否有人见过这种腐败原因,但我可以想象它会引起问题。我想的是:

//----- base.h -----
// nothing declared as virtual here!!
class Base {
public:
  Base();
  ~Base();
};
Base* getPointer();

//----- derived.cpp -----
class Derived: public Base {
public:
  Derived();
  ~Derived();
};
Base* getPointer() {
    return new Derived();
}

//----- main.cpp -----
#include "base.h"
#include <vector>
int main() {
  std::vector<Base*> v;
  v.push_back(getPointer());
  for (std::vector<Base*>::iterator i=v.begin(); i!=v.end(); ++i) {
    delete *i; // Derived::~Derived() is not invoked here
  }
  v.clear();
  return 0;
}

这可能不是这种情况,但我想我会提到它以防万一。

答案 5 :(得分:0)

正如其他人所提到的,当你在迭代器上调用delete时,你会删除它指向的数据,而不是指针本身。您应该确保将指针本身设置为NULL,或者使用向量上的erase方法来删除指针。

如果容器是指针容器,则可能会出现问题。你刚刚删除了一个指向指针的指针,但现在是什么?我们如何访问或释放内存?

答案 6 :(得分:0)

迭代器不会失效(除非您修改元素顺序)。

但我还有其他两个建议/最佳实践:

  1. 使用预增量而不是后增量来迭代矢量。后增量将始终创建当前元素的临时副本,这可能很昂贵! (对于指针而言无关紧要......,但你应该总是使用pre-inc!)。
  2. 不要使用普通指针向量。使用引用计数智能指针的向量。 shared_ptr将包含在C ++ 0x中,几乎所有当前的c ++实现(VC8,gcc,intel,...)都已经拥有它(它也可以通过boost获得)。智能指针拥有该对象,因此观察其生命周期(如果不再需要则删除它。所以你不需要小心......
  3. 性能影响非常小(只有一个级别的调用间接)..

    typedef vector<shared_ptr<ObjectType> > MyObjVector;
    MyObjVector Vec;
    
    for (It = Vec.begin(); It != Vec.end(); ++It)
    {
      GoToOtherCode(*It);
    }
    
    function GoToOtherCode (shared_ptr<ObjectType>& PObj)
    {
      // no delete needed!
    }
    

答案 7 :(得分:0)

实际上,迭代器在向量重新分配发生时无效。在向量初始化期间,它实际上最初保留了一些内存,但是当这个内存全部被向量用完时,整个向量在内存中重新分配,从而使所有迭代器无效。