71

有什么方法可以检查迭代器(无论它来自向量、列表、双端队列......)是否(仍然)可取消引用,即尚未失效?

我一直在使用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 it points to somewhere meaningful */
if (itd != l.end())
{
    //  blablabla
}
4

12 回答 12

65

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

于 2010-01-14T08:37:10.970 回答
26

正如 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
于 2010-01-14T08:37:44.190 回答
13

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

Visual Studio 的 STL 迭代器有一个“调试”模式可以做到这一点。您不希望在船舶构建中启用此功能(有开销),但在检查构建中很有用。

在此处阅读有关 VC10的信息(该系统可以而且实际上确实会更改每个版本,因此请查找特定于您的版本的文档)。

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

于 2010-01-14T16:49:55.007 回答
10

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

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

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

于 2010-01-14T08:33:17.747 回答
7

有什么方法可以检查迭代器(无论是来自向量、列表、双端队列...)是否(仍然)可取消引用,即尚未失效?

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

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

  • 如果在您的线程迭代时存在其他线程可能修改容器的风险,那么为了使这种情况线程安全,您的线程必须在容器上获取​​某种锁(这样它就可以防止其他线程在修改容器时它正在使用迭代器)

诸如捕获异常之类的变通方法将不起作用。

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

于 2010-01-14T08:54:09.443 回答
3

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

什么 alemjerus 说,迭代器总是可以被取消引用。不管下面有什么丑陋。很有可能迭代到内存的其他区域并写入可能保留其他对象的其他区域。我一直在看代码,无缘无故地观察变量的变化。这是一个很难检测到的错误。

记住插入和删除元素可能会使所有引用、指针和迭代器失效。

我最好的建议是让你的迭代器处于控制之下,并且总是在手边保留一个“结束”迭代器,以便能够测试你是否处于“行尾”。

于 2010-01-14T08:36:39.297 回答
2

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

当您执行以下操作时,迭代器会在传递给擦除函数之前先进行。

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

于 2010-03-07T06:02:26.570 回答
2

有什么方法可以检查迭代器是否可取消引用

是的,gcc调试容器可用作 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)
于 2017-11-12T18:36:50.630 回答
0

任何 std 容器的擦除函数的参数类型(正如您在问题中列出的那样,即它是否来自向量、列表、双端队列......)始终是该容器迭代器。

这个函数使用第一个给定的迭代器从容器中排除这个迭代器指向的元素,甚至是后面的元素。一些容器只擦除一个迭代器的一个元素,而另一些容器则擦除一个迭代器之后的所有元素(包括该迭代器指向的元素)直到容器的末尾。如果擦除函数接收到两个迭代器,则每个迭代器指向的两个元素都从容器中擦除,它们之间的所有其余元素也从容器中擦除,但关键是传递给擦除的每个迭代器任何 std 容器的功能都会失效!还有

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

这意味着指向某个已从容器中删除的元素的迭代器无法与 container.end() 进行比较。这个迭代器是无效的,因此它是不可解引用的,即不能使用 * 和 -> 操作符,它也不能递增,即不能使用 ++ 操作符,它也不能递减,即不能使用运营商。

也没有可比性!!!IE 你甚至不能使用 == 和 != 运算符

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

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

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

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

我自己制作了一个函数,可以检查、测试、知道并返回给定迭代器是否无效。您可以使用 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".
}

在我将它发布到那里之前,我已经测试过这个功能,发现这个功能对我有用。

我非常希望我已经完全回答了您的问题,也对您有很大帮助!

于 2015-01-14T17:23:00.807 回答
0

有办法,但是很丑……可以用这个std::distance功能

  #include <algorithms>
  using namespace std
  auto distance_to_iter = distance(container.begin(), your_iter);
  auto distance_to_end = distance(container.begin(),container.end());
  
  bool is_your_iter_still_valid = distance_to_iter != distance_to_end;
于 2021-10-29T00:57:15.583 回答
-1

使用带有增量的擦除:

   如果(某事)l.erase(itd++);

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

于 2010-01-20T17:03:13.243 回答
-3
if (iterator != container.end()) {
   iterator is dereferencable !
}

如果您的迭代器不等于container.end(),并且不可取消引用,那么您做错了什么。

于 2010-01-14T08:34:08.600 回答