检查迭代器是否有效
有没有什么方法可以检查迭代器(无论是来自向量、列表、双端队列...)是否(仍然)可取消引用,即尚未失效?
我一直在使用 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
}
Is there any way to check if an iterator (whether it is from a vector, a list, a deque...) is (still) dereferenceable, i.e. has not been invalidated?
I have been using try
-catch
, but is there a more direct way to do this?
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 it points to somewhere meaningful */
if (itd != l.end())
{
// blablabla
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
我假设你的意思是“迭代器是否有效”,它没有由于容器的更改而失效(例如,向向量插入/删除向量)。在这种情况下,不,您无法确定迭代器是否可以(安全地)取消引用。
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.
正如 jdehaan 所说,如果迭代器没有失效并指向容器,您可以通过将其与
container.end()
进行比较来进行检查。但请注意,如果迭代器是单数 - 因为它未初始化或在容器上进行变异操作后变得无效(例如,当您增加向量的容量时,向量的迭代器将失效) ) -- 唯一允许您对其执行的操作是赋值。换句话说,您无法检查迭代器是否是单数。
As jdehaan said, if the iterator wasn't invalidated and points into a container, you can check by comparing it to
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.
不可移植的答案:是的 - 在 Visual Studio 中,
Visual Studio 的 STL 迭代器有一个“调试”模式,它正是执行此操作。您不想在船舶构建中启用此功能(存在开销),但在检查构建中很有用。
在此处阅读 VC10 上的相关信息(该系统可以事实上,每个版本都会发生变化,因此请查找特定于您的版本的文档)。
编辑 另外,我应该补充一点:Visual Studio 中的调试迭代器被设计为在使用它们时立即爆炸(而不是未定义的行为);不允许“查询”其状态。
Non-portable answer: Yes - in 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.
Read about it on VC10 here (this system can and in fact does change every release, so find the docs specific to your version).
Edit Also, 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.
通常,您通过检查它是否与 end() 不同来测试它,例如
此外,使用异常处理来替换逻辑在设计和性能方面都是不好的。您的问题非常好,绝对值得在您的代码中进行替换。如名称所示的异常处理仅适用于罕见的意外问题。
Usually you test it by checking if it is different from the end(), like
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.
不,没有。相反,您需要在迭代器存在时控制对容器的访问,例如:
当容器仍在使用该容器的实例化迭代器时,您的线程不应修改容器(使迭代器无效)
如果存在其他线程在您的线程迭代时可能修改容器的风险,那么为了使这种情况成为线程安全的您的线程必须获取容器上的某种锁(以便防止其他线程在使用迭代器时修改容器)
诸如捕获异常之类的解决方法将不起作用。
这是更普遍的问题的一个具体实例,“我可以测试/检测指针是否有效吗?”,答案通常是“不,你不能测试它:相反,你必须管理所有内存分配”和删除,以便知道任何给定的指针是否仍然有效”。
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 know whether any given pointer is still valid".
尝试和捕获并不安全,如果你的迭代器“越界”,你不会,或者至少很少抛出。
正如 alemjerus 所说,迭代器总是可以被取消引用。不管下面隐藏着什么丑陋。很有可能迭代到内存的其他区域并写入可能保留其他对象的其他区域。我一直在查看代码,观察变量的变化,没有任何特殊原因。这是一个很难检测到的错误。
另外,明智的做法是记住插入和删除元素可能会使所有引用、指针和迭代器无效。
我最好的建议是让你的迭代器处于控制之下,并且总是手边保留一个“结束”迭代器,以便能够测试你是否处于“行尾”。
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.
Also it is wise to remember that inserting and removing elements might potentially invalidate all references, 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.
是的,使用 gcc 调试容器可作为 GNU 扩展使用。对于
std::list
,您可以使用__gnu_debug::list
。一旦尝试使用无效迭代器,以下代码将立即中止。由于调试容器会产生额外的开销,因此它们仅在调试时才有意义。Yes, with gcc debugging containers available as GNU extensions. For
std::list
you can use__gnu_debug::list
instead. 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.在某些 STL 容器中,当您擦除迭代器的当前值时,当前迭代器将变得无效。发生这种情况是因为擦除操作更改了容器的内部内存结构,并且现有迭代器上的增量运算符指向未定义的位置。
当您执行以下操作时,迭代器在传递给擦除函数之前会被增加。
如果(某事)l.erase(itd++);
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.
When you do the following, iterator is incementented before it is passed to erase function.
if (something) l.erase(itd++);
任何 std 容器的擦除函数的参数类型(正如您在问题中列出的那样,即是否来自向量、列表、双端队列...)是 always 的迭代器仅限此容器。
此函数使用第一个给定的迭代器从容器中排除该迭代器指向的元素,甚至排除后面的元素。有些容器只擦除一个迭代器的一个元素,而另一些容器则擦除一个迭代器后面的所有元素(包括该迭代器指向的元素)到容器末尾的所有元素。如果擦除函数接收两个迭代器,则每个迭代器指向的两个元素将从容器中删除,并且它们之间的所有其余元素也将从容器中删除,但要点是每个传递给任何 std 容器的擦除函数的迭代器将变得无效! 另外:
每个指向已从容器中删除的某个元素的迭代器都会变得无效,但它不会通过容器的末尾!
这意味着指向已从容器中删除的某些元素的迭代器无法与 container.end() 进行比较。
该迭代器无效,因此它不可取消引用,即您不能使用 * 或 ->;运算符,它也是不可递增的,即您不能使用 ++ 运算符,它也是不可递减的,即您不能使用 -- 运算符。
也没有可比性!!! IE 中,您甚至不能使用 == 或 != 运算符
实际上,您不能使用在 std 迭代器中声明和定义的任何运算符。
你不能用这个迭代器做任何事情,比如空指针。
使用无效迭代器执行某些操作会立即停止程序,甚至导致程序崩溃并出现断言对话框窗口。无论您选择什么选项、单击什么按钮,都无法继续程序。您只需单击“中止”按钮即可终止程序和进程。
您不会对无效迭代器执行任何其他操作,除非您可以将其设置为容器的开头,或者只是忽略它。
但在决定如何处理迭代器之前,首先您必须知道如果调用正在使用的容器的擦除函数,该迭代器是否无效。
我自己创建了一个函数,用于检查、测试、了解并返回 true 给定的迭代器是否无效。您可以使用 memcpy 函数来获取任何对象、项目、结构、类等的状态,当然,我们总是首先使用 memset 函数来清除或清空新的缓冲区、结构、类或任何对象或项目:
在我将其发布到此处之前,我已经测试过此功能,并发现此功能对我有用。
我非常希望我已经完全回答了您的问题并且也为您提供了很大的帮助!
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 always iterator of this container 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 that every iterator that is passed to the erase function of any std container becomes invalid! Also:
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.
It is also not comparable!!! I.E. you cannot even use neither == nor != operators
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.
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:
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!
使用带有增量的擦除:
这样您就可以测试迭代器的有效性。
use erase with increment :
so you can test the validity of the iterator.
有一种方法,但是很难看......你可以使用
std::distance
函数There is a way, but is ugly... you can use the
std::distance
function试试这个:
Try this:
如果你的迭代器不等于container.end(),并且不可取消引用,那么你就做错了。
If your iterator doesnt equal
container.end()
, and is not dereferencable, youre doing something wrong.