26.STL中迭代器失效的情况有哪些?

26.STL中迭代器失效的情况有哪些?

1.STL之vector迭代器失效

std::vector是一个动态数组,当其大小或容量发生改变时,其内存可能被重新分配,从而导致已经存在的迭代器失效。以下是几种可能导致迭代器失效的情况:

1.插入或添加元素:使用push_backinsertstd::vector中插入元素可能导致所有的迭代器失效。这是因为,如果std::vector的当前容量不足以容纳新的元素,std::vector会分配新的内存空间,并将所有的元素移动到新的内存空间,导致原有迭代器失效。

std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin();
vec.push_back(6); // 可能会导致 vec 的内存被重新分配

// 这时,迭代器 it 可能已经失效了,它仍然指向旧的内存块,而不是新的内存块。
// 所以,如果我们尝试使用它,可能会出现未定义的行为。
std::cout << *it << std::endl; // 未定义的行为

在这个例子中,我们在遍历的过程中向vec的尾部添加了元素,这可能导致it迭代器失效,因为push_back可能导致vector的内存被重新分配。这样的代码是有问题的,应当避免在遍历过程中修改vector的大小。

2.删除元素:使用pop_backeraseclear删除元素,会导致指向被删除元素及其之后元素的迭代器失效。

当你从 std::vector 中删除元素时,这将导致指向被删除元素及其之后元素的迭代器失效。下面是一个具体的例子:

std::vector<int> vec = {1, 2, 3, 4, 5};

auto it = std::find(vec.begin(), vec.end(), 3);  // it 现在指向元素 3

vec.erase(it);  // 删除元素 3

// it 现在是一个失效的迭代器,因为它指向的元素已经被删除了。如果我们尝试使用它,会产生未定义的行为。
std::cout << *it << std::endl;  // 未定义的行为!

在这个例子中,我们首先获取了一个指向元素3的迭代器it。然后,我们调用erase删除了元素3,这导致it失效。最后,我们尝试使用it,但这是未定义的行为,可能导致程序崩溃。

因此,在删除元素之后,我们应当避免使用指向被删除元素的迭代器。如果你需要在删除元素后继续访问vector中的元素,你应当在删除操作后重新获取迭代器。例如,erase函数返回的是一个指向被删除元素之后元素的有效迭代器,你可以使用这个迭代器来继续访问vector中的元素。

std::vector<int> vec = {1, 2, 3, 4, 5};

auto it = std::find(vec.begin(), vec.end(), 3);

it = vec.erase(it);  // erase 返回的是一个有效的迭代器,指向被删除元素之后的元素

if(it != vec.end()) 
{
    std::cout << *it << std::endl;  // 这是安全的
}

3.调整容器大小:使用resizeshrink_to_fit改变std::vector的大小可能会导致所有的迭代器失效,因为这可能会触发内存的重新分配。

调整std::vector的大小,比如使用resize()shrink_to_fit(),可能会使迭代器失效。原因是,这些操作可能会改变vector的内部内存布局。让我们看一下具体的例子:

std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2;  // 现在,it 指向元素 3

vec.resize(2);  // 调整 vector 的大小,使其只包含前两个元素

// it 现在是失效的迭代器,因为它指向的元素已经被删除了。如果我们尝试使用它,将会产生未定义的行为。
std::cout << *it << std::endl;  // 未定义的行为!

在这个例子中,我们首先获得一个指向元素3的迭代器it。然后,我们调用resize(2),将vector的大小调整为2,这会删除除前两个元素以外的所有元素。因此,it现在是失效的,因为它指向的元素已经被删除了。最后,我们尝试使用it,这是未定义的行为,可能会导致程序崩溃。

因此,调整std::vector的大小后,应该避免使用可能已经失效的迭代器。如果你需要在调整大小后继续访问vector中的元素,你应该在调整大小操作后重新获取迭代器。

4.调换元素:使用swapstd::swap交换两个vector,会导致相关vector的迭代器失效。

在使用迭代器遍历std::vector时,一定要特别注意,避免在遍历过程中进行插入、删除等可能使迭代器失效的操作,否则可能会导致未定义的行为。

在C++的std::vector中,你可以使用std::swap或成员函数swap()来交换两个向量的内容。这种操作会使得与被交换的向量相关联的所有迭代器、引用和指针失效。以下是一个具体的例子:

std::vector<int> vec1 = {1, 2, 3, 4, 5};
std::vector<int> vec2 = {6, 7, 8, 9, 10};

auto it1 = vec1.begin(); // it1 现在指向 vec1 的第一个元素
auto it2 = vec2.begin(); // it2 现在指向 vec2 的第一个元素

std::swap(vec1, vec2); // 或者 vec1.swap(vec2);

// 此时,vec1 包含 vec2 的元素,vec2 包含 vec1 的元素

// it1 和 it2 现在是失效的,因为它们指向的内存已经被交换了
std::cout << *it1 << std::endl; // 未定义的行为
std::cout << *it2 << std::endl; // 未定义的行为

在上述代码中,it1原本指向vec1的首个元素,it2原本指向vec2的首个元素。当我们交换vec1vec2后,vec1的所有元素变成了vec2原本的元素,反之亦然。这就意味着it1现在实际上指向了vec2原本的内存,而it2指向了vec1原本的内存。由于这两块内存现在可能已经被重新分配给其他对象,或者被释放掉了,所以尝试访问*it1*it2会产生未定义的行为。

因此,当你交换两个std::vector后,应该假定所有的迭代器、引用和指针都已经失效,并在需要的时候重新获取它们。

posted @ 2023-08-03 07:29  CodeMagicianT  阅读(297)  评论(0编辑  收藏  举报