C++ 检查迭代器是否有效

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2062956/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:59:18  来源:igfitidea点击:

Checking if an iterator is valid

c++stliteratordereference

提问by huff

Is there any way to check if an iterator (whether it is from a vector, a list, a deque...) is (still) dereferencable, i.e. has not been invalidated?

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

I have been using try-catch, but is there a more direct way to do this?

我一直在使用try- catch,但是有没有更直接的方法来做到这一点?

Example: (which doesn't work)

示例:(这不起作用)

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
}

采纳答案by Jason Govig

I assume you mean "is an iterator valid," that it hasn't been invalidated due to changes to the container (e.g., inserting/erasing to/from a vector). In that case, no, you cannot determine if an iterator is (safely) dereferencable.

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

回答by avakar

As jdehaan said, if the iterator wasn't invalidated and points into a container, you can check by comparing it to container.end().

正如 jdehaan 所说,如果迭代器没有失效并指向容器,您可以通过将其与container.end().

Note, however, that if the iterator is singular-- because it wasn't initialized or it became invalid after a mutating operation on the container (vector's iterators are invalidated when you increase the vector's capacity, for example) -- the only operation that you are allowed to perform on it is assignment. In other words, you can't check whether an iterator is singular or not.

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

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

回答by Terry Mahaffey

Non-portable answer: Yes - in Visual Studio

不可移植的答案:是 - 在 Visual Studio 中

Visual Studio's STL iterators have a "debugging" mode which do exactly this. You wouldn't want to enable this in ship builds (there is overhead) but useful in checked builds.

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

Read about it on VC10 here(this system can and in fact does change every release, so find the docs specific to your version).

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

EditAlso, I should add: debug iterators in visual studio are designed to immediately explode when you use them (instead undefined behavior); not to allow "querying" of their state.

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

回答by jdehaan

Usually you test it by checking if it is different from the end(), like

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

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

Moreover using exception handling for replacing logic is bad in terms of design and performance. Your question is very good and it is definitively worth a replacement in your code. Exception handling like the names says shall only be used for rare unexpected issues.

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

回答by ChrisW

Is there any way to check if a iterator (whether it is from a vector, a list, a deque...) is (still) dereferencable, i.e has not been invalidated ?

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

No, there isn't. Instead you need to control access to the container while your iterator exists, for example:

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

  • Your thread should not modify the container (invalidating the iterator) while it is still using an instantiated iterator for that container

  • If there's a risk that other threads might modify the container while your thread is iterating, then in order to make this scenario thread-safe your thread must acquire some kind of lock on the container (so that it prevents other threads from modifying the container while it's using an iterator)

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

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

Work-arounds like catching an exception won't work.

像捕获异常这样的变通办法是行不通的。

This is a specific instance of the more general problem, "can I test/detect whether a pointer is valid?", the answer to which is typically "no, you can't test for it: instead you have to manage all memory allocations and deletions in order to knowwhether any given pointer is still valid".

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

回答by daramarak

Trying and catching is not safe, you will not, or at least seldom throw if your iterator is "out of bounds".

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

what alemjerus say, an iterator can always be dereferenced. No matter what uglyness lies beneath. It is quite possible to iterate into other areas of memory and write to other areas that might keep other objects. I have been looking at code, watching variables change for no particular reason. That is a bug that is really hard to detect.

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

Also it is wise to remember that inserting and removing elements might potentially invalidate allreferences, pointers and iterators.

此外,明智的做法是记住插入和删除元素可能会使所有引用、指针和迭代器无效。

My best advice would be to keep you iterators under control, and always keep an "end" iterator at hand to be able to test if you are at the "end of the line" so to speak.

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

回答by ks1322

Is there any way to check if an iterator is dereferencable

有没有办法检查迭代器是否可取消引用

Yes, with gcc debugging containersavailable as GNU extensions. For std::listyou can use __gnu_debug::listinstead. The following code will abort as soon as invalid iterator is attempted to be used. As debugging containers impose extra overhead they are intended only when debugging.

是的,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)

回答by lava

In some of the STL containers, the current iterator becomes invalid when you erase the current value of the iterator. This happens because the erase operation changes the internal memory structure of the container and increment operator on existing iterator points to an undefined locations.

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

When you do the following, iterator is incementented before it is passed to erase function.

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

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

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

回答by lava

The type of the parameters of the erase function of any std container (as you have listed in your question, i.e. whether it is from a vector, a list, a deque...) is alwaysiterator of this container only.

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

This function uses the first given iterator to exclude from the container the element that this iterator points at and even those that follow. Some containers erase only one element for one iterator, and some other containers erase all elements followed by one iterator (including the element pointed by this iterator) to the end of the container. If the erase function receives two iterators, then the two elements, pointed by each iterator, are erased from the container and all the rest between them are erased from the container as well, but the point is thatevery iterator that is passed to the erase function of any std container becomes invalid! Also:

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

Each iterator that was pointing at some element that has been erased from the container becomes invalid, but it doesn't pass the end of the container!

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

This means that an iterator that was pointing at some element that has been erased from the container cannot be compared to container.end(). This iterator is invalid, and so it is not dereferencable, i.e. you cannot use neither the * nor -> operators, it is also not incrementable, i.e. you cannot use the ++ operator, and it is also not decrementable, i.e. you cannot use the -- operator.

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

It is also not comparable!!! I.E. you cannot even use neither == nor != operators

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

Actually you cannot use any operator that is declared and defined in the std iterator. You cannot do anything with this iterator, like null pointer.

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

Doing something with an invalid iterator immediately stops the program and even causes the program to crash and an assertion dialog window appears. There is no way to continue program no matter what options you choose, what buttons you click. You just can terminate the program and the process by clicking the Abort button.

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

You don't do anything else with an invalid iterator, unless you can either set it to the begin of the container, or just ignore it.

您不会对无效的迭代器做任何其他事情,除非您可以将其设置为容器的开头,或者只是忽略它。

But before you decide what to do with an iterator, first you must know if this iterator is either invalid or not, if you call the erase function of the container you are using.

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

I have made by myself a function that checks, tests, knows and returns true whether a given iterator is either invalid or not. You can use the memcpy function to get the state of any object, item, structure, class and etc, and of course we always use the memset function at first to either clear or empty a new buffer, structure, class or any object or item:

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

I have already tested this function before I posted it there and found that this function is working for me.

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

I very hope that I have fully answered your question and also helped you very much!

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

回答by lsalamon

use erase with increment :

使用带增量擦除:

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

so you can test the validity of the iterator.

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