1

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

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

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

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

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

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

在此先感谢您的时间。

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

4

8 回答 8

8

它不会使迭代器无效,这实际上是您删除向量拥有的堆分配对象的方式, clear() 方法不会为您执行此操作。这很常见:

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

Vec.clear();

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

于 2009-09-30T00:18:04.680 回答
3

它不会使迭代器无效,但指针本身仍然指向现在已删除的对象,因此您确实需要做一些事情来确保您的代码不会在现在悬空的指针上绊倒。例如,您可以将指针设置为空值,或用于erase从向量中删除已删除的项目。

于 2009-09-30T00:11:01.027 回答
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;
}

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

于 2009-09-30T00:19:43.027 回答
1

这不应该使您的迭代器无效-但是...

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

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

于 2009-09-30T00:10:23.277 回答
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;
}

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

于 2009-09-30T01:49:28.927 回答
0

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

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

于 2009-10-01T00:46:07.780 回答
0

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

但我确实有另外两个建议/最佳实践:

  1. 使用前增量而不是后增量来遍历您的向量。后增量将始终创建当前元素的临时副本,这可能很昂贵!(对于指针来说,这并不重要......,但你应该总是使用 pre-inc!)。
  2. 不要使用普通指针向量。使用引用计数智能指针向量。shared_ptr将包含在 C++0x 中,几乎所有当前的 c++ 实现(VC8、gcc、intel、...)都已经拥有它(它也可以通过 boost 获得)。智能指针拥有该对象,因此会观察它的生命周期(如果不再需要则将其删除。所以你不需要照顾......

性能影响非常小(只有一级调用间接)..

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!
}
于 2009-10-02T08:13:55.707 回答
0

实际上,当向量重新分配发生时,迭代器会失效。在 vector 的初始化过程中,它实际上最初保留了一些内存,但是当你的内存全部被 vector 用完时,整个 vector 被重新分配到内存中,从而使所有迭代器失效。

于 2009-11-09T07:33:36.613 回答