当insert
-ing 进入std::vector
C++ 标准时,只要capacity
没有用尽,插入点之前的所有迭代器都保持有效(参见 [23.2.4.3/1] 或std::vector 迭代器失效)。
不允许插入点之后的迭代器保持有效(如果容量没有用尽)的原因是什么?当然,它们随后会指向不同的元素,但是(从 的假定实现std::vector
)应该仍然可以使用这样的迭代器(例如取消引用它或增加它)。
当insert
-ing 进入std::vector
C++ 标准时,只要capacity
没有用尽,插入点之前的所有迭代器都保持有效(参见 [23.2.4.3/1] 或std::vector 迭代器失效)。
不允许插入点之后的迭代器保持有效(如果容量没有用尽)的原因是什么?当然,它们随后会指向不同的元素,但是(从 的假定实现std::vector
)应该仍然可以使用这样的迭代器(例如取消引用它或增加它)。
您似乎将“无效”迭代器视为仅在使用时会引发崩溃的迭代器,但标准的定义更广泛。它包括迭代器仍然可以安全地被取消引用,但不再指向它预期指向的元素的可能性。(这是观察到“未定义的行为”并不意味着“您的程序将立即崩溃”的一个特殊情况;它也可能意味着“您的程序将默默地计算错误的结果”甚至“在此实现中不会发生明显的错误” .")
更容易证明为什么这是一个问题erase
:
#include <vector>
#include <iostream>
int main(void)
{
std::vector<int> a { 0, 1, 2, 3, 4, 4, 6 };
for (auto p = a.begin(); p != a.end(); p++) // THIS IS WRONG
if (*p == 4)
a.erase(p);
for (auto p = a.begin(); p != a.end(); p++)
std::cout << ' ' << *p;
std::cout << '\n';
}
在 C++ 的典型实现中,这个程序不会崩溃,但它会打印0 1 2 3 4 6
,而不是0 1 2 3 6
像预期的那样,因为擦除第一个4
无效 p
- 通过将它推进到第二个4
。
您的 C++ 实现可能有一个特殊的“调试”模式,在这种模式下,该程序在运行时确实会崩溃。例如,对于 GCC 4.8:
$ g++ -std=c++11 -W -Wall test.cc && ./a.out
0 1 2 3 4 6
但
$ g++ -std=c++11 -W -Wall -D_GLIBCXX_DEBUG test.cc && ./a.out
/usr/include/c++/4.8/debug/safe_iterator.h:307:error: attempt to increment
a singular iterator.
Objects involved in the operation:
iterator "this" @ 0x0x7fff5d659470 {
type = N11__gnu_debug14_Safe_iteratorIN9__gnu_cxx17__normal_iteratorIPiNSt9__cxx19986vectorIiSaIiEEEEENSt7__debug6vectorIiS6_EEEE (mutable iterator);
state = singular;
references sequence with type `NSt7__debug6vectorIiSaIiEEE' @ 0x0x7fff5d659470
}
Aborted
请务必理解,无论哪种方式,该程序都会引发未定义的行为。只是未定义行为的后果在调试模式下更为显着。
向量是动态增长的,所以当你推入向量时,如果项目没有空间,则需要为其分配内存。标准要求vector
必须将其元素存储在连续的内存中,因此在分配内存时,它必须足以存储所有现有元素以及新元素。
向量本身不知道任何迭代器,因此无法将它们更新到新的元素存储中。因此,在重新分配内存后迭代器无效。
迭代器可能引用不同的元素足以使它们失效。迭代器应该在其有效生命周期内引用相同的元素。
你是对的,在实践中,如果你要取消引用这样的迭代器,你可能不会遇到任何崩溃或鼻恶魔,但这并不意味着它有效。
向量不知道存在哪些迭代器。然而,插入元素后元素的内存位置发生了变化。这意味着,如果迭代器保持有效,则需要更新迭代器以反映该更改。但是向量不能做这个更新,因为它不知道存在哪些迭代器。