检查迭代器是否有效

有没有办法检查迭代器(是否来自向量、列表、 deque...)是否(仍然)可解引用,也就是说没有失效?

我一直在使用 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
}
96548 次浏览

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

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

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

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

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

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

就像 alemjerus 说的,迭代器总是可以解引用的。不管底下有多丑陋。迭代到内存的其他区域并写入可能保留其他对象的其他区域是很有可能的。我一直在研究代码,看着变量无缘无故地变化。这是一个很难检测到的错误。

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

我最好的建议是保持迭代器处于控制之下,并且始终保持一个“ end”迭代器在手边,以便能够测试您是否处于可以这么说的“ end of the line”。

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

正如 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

有没有办法检查迭代器(是否来自向量、列表、 deque...)是否(仍然)可以取消引用,即没有失效?

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

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

  • 如果存在其他线程可能在您的线程迭代时修改容器的风险,那么为了使这个场景线程安全,您的线程必须获得对容器的某种锁(这样就可以防止其他线程在使用迭代器时修改容器)

像捕捉异常这样的变通方法不会起作用。

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

不可移植的答案: Yes-in Visual Studio

VisualStudio 的 STL 迭代器有一个“调试”模式,正是这样做的。您不希望在船舶构建中启用此功能(存在开销) ,但在检查的构建中很有用。

在 VC10给你上阅读有关它的内容(这个系统可以而且实际上确实更改了每个版本,所以请找到特定于您的版本的文档)。

编辑 另外,我还应该补充一点: 视觉工作室中的调试迭代器被设计成当你使用它们时立即爆炸(而不是未定义行为) ,不允许“查询”它们的状态。

以递增方式使用擦除:

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

因此您可以测试迭代器的有效性。

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

执行下列操作时,迭代器在被传递到擦除函数之前会被逐步递增。

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

任何 std 容器的擦除函数的参数类型(正如您在问题中列出的,即它是否来自向量、列表、 deque...)是该容器 只有一直都是迭代器。

这个函数使用第一个给定的迭代器从容器中排除这个迭代器指向的元素,甚至排除后面的元素。一些容器只为一个迭代器擦除一个元素,而另一些容器擦除一个迭代器后面的所有元素(包括该迭代器指向的元素) ,直到容器的末尾。如果擦除函数接收到两个迭代器,那么由每个迭代器指向的两个元素将从容器中擦除,它们之间的所有其他元素也将从容器中擦除,传递给任何 std 容器的擦除函数的每个迭代器都将变为无效!返回文章页面

每个迭代器都指向一些从容器中被擦除的元素,这些元素将变得无效,但是它不会传递容器的末尾!

这意味着指向从容器中擦除的某个元素的迭代器不能与 conter.end ()进行比较。 这个迭代器是无效的,所以它不能解引用,也就是说你不能使用 * nor-> 操作符,它也不能递增,也就是说你不能使用 + + 操作符,它也不能递减,也就是说你不能使用——操作符。

它也没有可比性! ! ! 也就是说,你甚至不能使用 = = or! = 运算符

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

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

除非可以将无效迭代器设置为容器的开始,或者直接忽略它,否则不能对它进行其他操作。

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

我自己创建了一个函数来检查、测试、知道并返回 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".
}

在发布之前,我已经测试过这个函数,发现这个函数对我很有用。

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

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

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

有一个方法,但是很难看... 你可以使用 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;