检查迭代器是否有效

时间:2010-01-14 08:30:25

标签: c++ stl iterator dereference

有没有办法检查迭代器(无论是来自向量,列表,双端队列......)是否(仍)可解除引用,即未被无效?

我一直在使用try - catch,但是有更直接的方式吗?

示例:(不起作用)

list<int> l;
for (i = 1; i<10; i++) {
    l.push_back(i * 10);
}

itd = l.begin();
itd++;
if (something) {
    l.erase(itd);
}

/* now, in other place.. check if itd points to somewhere meaningful */
if (itd != l.end())
{
    //  blablabla
}

11 个答案:

答案 0 :(得分:57)

我假设你的意思是“迭代器是有效的”,它由于容器的更改而没有失效(例如,插入/擦除向量)。在这种情况下,不,您无法确定迭代器是否(安全地)可解除引用。

答案 1 :(得分:25)

正如jdehaan所说,如果迭代器没有失效并指向容器,你可以通过将其与container.end()进行比较来检查。

但是,请注意,如果迭代器是单数 - 因为它没有被初始化,或者在对容器进行变异操作后变为无效(当你增加向量的容量时,向量的迭代器会失效) ,例如) - 允许您执行的唯一操作是赋值。换句话说,你无法检查迭代器是否是单数。

std::vector<int>::iterator iter = vec.begin();
vec.resize(vec.capacity() + 1);
// iter is now singular, you may only perform assignment on it,
// there is no way in general to determine whether it is singular or not

答案 2 :(得分:10)

非便携式答案:是 - 在Visual Studio中

Visual Studio的STL迭代器具有“调试”模式,正是这样做的。您不希望在发布版本中启用此功能(存在开销),但在已检查的版本中非常有用。

在VC10 here上阅读它(此系统可以并且实际上确实会更改每个版本,因此请查找特定于您的版本的文档)。

编辑另外,我应该补充一点:Visual Studio中的调试迭代器设计为在你使用它们时立即爆炸(而不是未定义的行为);不允许“查询”他们的国家。

答案 3 :(得分:8)

通常你通过检查它是否与end()不同来测试它,比如

if (it != container.end())
{
   // then dereference
}

此外,在设计和性能方面使用异常处理来替换逻辑是不好的。您的问题非常好,在您的代码中绝对值得替换。像名称这样的异常处理只能用于罕见的意外问题。

答案 4 :(得分:6)

  

有没有办法检查迭代器(无论是来自向量,列表,双端队列......)是否(仍然)可解除引用,即没有失效?

不,没有。相反,您需要在迭代器存在时控制对容器的访问,例如:

  • 当线程仍在使用该容器的实例化迭代器时,您的线程不应修改容器(使迭代器无效)

  • 如果在线程迭代时存在其他线程可能修改容器的风险,那么为了使这个场景成为线程安全的,你的线程必须在容器上获得某种锁定(这样它才能阻止其他线程)在使用迭代器时修改容器的线程

捕获异常等解决办法是行不通的。

这是一个更普遍的问题的特定实例,“我可以测试/检测指针是否有效吗?”,答案通常是“不,你不能测试它:相反,你必须管理所有内存分配和删除,以便知道任何给定指针是否仍然有效“。

答案 5 :(得分:3)

尝试和捕获是不安全的,如果你的迭代器“超出界限”,你就不会,或者至少很少抛出。

alemjerus说,迭代器总是可以被解除引用。无论下面是什么丑陋。很有可能迭代到其他内存区域并写入可能保留其他对象的其他区域。我一直在看代码,看着变量没有特别的原因。这是一个很难检测的错误。

同样明智的是要记住插入和删除元素可能会使所有引用,指针和迭代器无效。

我最好的建议是让你的迭代器得到控制,并始终保持一个“结束”迭代器,以便能够测试你是否处于“行尾”可以这么说。

答案 6 :(得分:1)

在某些STL容器中,当您擦除迭代器的当前值时,当前迭代器将变为无效。发生这种情况是因为擦除操作会更改容器的内部存储器结构,并在现有迭代器上增加运算符指向未定义的位置。

当你执行以下操作时,迭代器在传递给擦除函数之前就会被初始化。

if (something) l.erase(itd++);

答案 7 :(得分:1)

  

有没有办法检查迭代器是否可以解除引用

是的,gcc debugging containers可用作GNU扩展。对于std::list,您可以使用__gnu_debug::list代替#include <debug/list> int main() { __gnu_debug::list<int> l; for (int i = 1; i < 10; i++) { l.push_back(i * 10); } auto itd = l.begin(); itd++; l.erase(itd); /* now, in other place.. check if itd points to somewhere meaningful */ if (itd != l.end()) { // blablabla } } $ ./a.out /usr/include/c++/7/debug/safe_iterator.h:552: Error: attempt to compare a singular iterator to a past-the-end iterator. Objects involved in the operation: iterator "lhs" @ 0x0x7ffda4c57fc0 { type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator); state = singular; references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0 } iterator "rhs" @ 0x0x7ffda4c580c0 { type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator); state = past-the-end; references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0 } Aborted (core dumped) 。一旦尝试使用无效迭代器,以下代码将中止。由于调试容器会产生额外的开销,因此只有在调试时才会这样做。

@yield('content')

答案 8 :(得分:0)

任何标准容器的擦除功能参数的类型(正如您在问题中列出的,即它是来自向量,列表,双端队列......)总是此容器的迭代器

此函数使用第一个给定的迭代器从容器中排除此迭代器指向的元素,甚至是后面的元素。一些容器只擦除一个迭代器的一个元素,而一些其他容器擦除所有元素后跟一个迭代器(包括该迭代器指向的元素)到容器的末尾。如果擦除函数接收到两个迭代器,那么每个迭代器指向的两个元素将从容器中删除,并且它们之间的所有其余元素也将从容器中删除,但重点是每个传递给任何std容器的erase函数的迭代器变得无效! 同时

指向已从容器中删除的某个元素的每个迭代器都将变为无效,但它不会通过容器的末尾!

这意味着指向已从容器中删除的某个元素的迭代器无法与container.end()进行比较。 这个迭代器是无效的,所以它不是可以解除引用的,即你不能同时使用*也不是&gt;运算符,它也是不可递增的,即你不能使用++运算符,它也是不可递减的,即你不能使用 - 运算符。

它也无法比较!!! I.E.你甚至不能使用== nor!=运算符

实际上,您不能使用在std迭代器中声明和定义的任何运算符。 你不能对这个迭代器做任何事情,比如空指针。

使用无效迭代器执行某些操作会立即停止程序,甚至会导致程序崩溃并显示断言对话框窗口。无论您选择哪种选项,单击哪些按钮,都无法继续程序。您只需单击“中止”按钮即可终止程序和过程。

除非您可以将其设置为容器的开头,或者只是忽略它,否则您不会对无效的迭代器执行任何其他操作。

但是在你决定如何处理迭代器之前,首先你必须知道这个迭代器是否无效,如果你调用你正在使用的容器的擦除功能。

我自己创建了一个函数,它检查,测试,知道并返回true给定的迭代器是否无效。你可以使用memcpy函数来获取任何对象,项目,结构,类等的状态,当然我们总是首先使用memset函数来清除或清空新的缓冲区,结构,类或任何对象或项目:

bool IsNull(list<int>::iterator& i) //In your example, you have used list<int>, but if your container is not list, then you have to change this parameter to the type of the container you are using, if it is either a vector or deque, and also the type of the element inside the container if necessary.
{
    byte buffer[sizeof(i)];
    memset(buffer, 0, sizeof(i));
    memcpy(buffer, &i, sizeof(i));
    return *buffer == 0; //I found that the size of any iterator is 12 bytes long. I also found that if the first byte of the iterator that I copy to the buffer is zero, then the iterator is invalid. Otherwise it is valid. I like to call invalid iterators also as "null iterators".
}

我在发布之前已经测试了这个功能,发现这个功能对我有用。

我非常希望我已经完全回答了你的问题,并且非常帮助你!

答案 9 :(得分:-1)

if (iterator != container.end()) {
   iterator is dereferencable !
}

如果你的迭代器不等于container.end(),并且不是可以解除引用,那么你做错了。

答案 10 :(得分:-1)

使用带增量的擦除:

   if (something) l.erase(itd++);

所以你可以测试迭代器的有效性。