使用迭代器时在 STL 中删除和添加元素

问题描述

我对正在阅读的书中的一段内容感到困惑。

Excerpt Part A

Excerpt Part B

我不明白为什么这本书证明对 STL 使用移除和添加操作是合理的,即使它会导致迭代器无效。有人可以向我澄清这本书试图传达的信息吗?

解决方法

作者试图说明的一点是,虽然 vector::insertvector::erase 可能会使先前创建的迭代器无效,但它们返回的迭代器必然有效,因此您可以依赖它们。

检查此表以准确了解每个 STL 容器何时发生 iter/ref 失效:https://en.cppreference.com/w/cpp/container

,

即使这些操作使(一些)旧的、预先存在的迭代器失效,但这并不能阻止新创建的迭代器有效。示例程序将无效迭代器的值分配给(文本中使用的单词中的“重置”)一个有效值。

这是一个不同但类似的例子:

int* ptr;     // an iterator

{
    int i;
    ptr = &i; // ptr is now valid
} // ptr has become invalid because storage duration of i ended

int j;
ptr = &j; // ptr is valid again despite having been invalid earlier
,

在示例中,inserterase 的返回值分配给了 iter。这两种方法都会使容器中的现有迭代器无效,但它们返回的迭代器是有效的。

,

对于至少从 C++ 14 标准开始的初学者,您可以在成员函数 const_iteratorerase 中使用常量迭代器 insert

现在相应的成员函数(在您的代码片段中使用)声明如下

iterator insert(const_iterator position,const T& x);
iterator erase(const_iterator position);

所以你可以写

auto iter = vi.cbegin();

代替

auto iter = vi.begin();

并在 eraseinsert 的调用中使用它。

使用成员函数 insert 将新元素添加到向量后,向量可以在内部为其元素重新分配内存。在这种情况下,当前迭代器可能无效,因为它会在重新分配之前指向内存。

但该函数返回一个迭代器,该迭代器指向存储在已重新分配的内存中的新插入元素。因此返回的迭代器将指向内存的实际范围。

考虑以下演示程序。

#include <iostream>
#include <vector>
#include <iterator>

int main() 
{
    std::vector<int> v;
    
    v.insert( v.cbegin(),0 );
    
    std::cout << "v.data() = " << v.data() 
              << ",&v[0] = " << &v[0] << '\n';
    
    v.insert( v.cbegin(),1 );
    
    std::cout << "v.data() = " << v.data() 
              << ",&v[0] = " << &v[0]
              << ",&v[1] = " << &v[1] << '\n';
    
    
    return 0;
} 

它的输出可能看起来像

v.data() = 0x556699d79e70,&v[0] = 0x556699d79e70
v.data() = 0x556699d7aea0,&v[0] = 0x556699d7aea0,&v[1] = 0x556699d7aea4

如您所见,在插入第二个值后,向量在内部重新分配了存储元素的内存。即在插入第二个值之前,函数 cbegin 返回的当前迭代器指向地址为 0x556699d79e70 的内存范围。插入第二个值后,函数 cbegin 返回的当前迭代器将指向地址为 0x556699d7aea0 的内存范围。即之前的当前迭代器失效。

但是如果你按照下面的方式重写程序。

#include <iostream>
#include <vector>
#include <iterator>

int main() 
{
    std::vector<int> v;
    auto iter = v.cbegin();
    
    iter = v.insert( iter,0 );
    
    std::cout << "v.data() = " << v.data()  
              << ",&*iter = " << &*iter << '\n';
    
    iter = v.insert( iter,&*iter = " << &*iter << '\n';
    
    
    return 0;
}

然后从程序输出中看到

v.data() = 0x55d8c5698e70,&*iter = 0x55d8c5698e70
v.data() = 0x55d8c5699ea0,&*iter = 0x55d8c5699ea0

迭代器 iter 始终指向有效的内存范围,因为它被函数 insert 返回的迭代器重新分配,结果指向有效的内存范围。

同样适用于成员函数erase。函数迭代器返回的无效。