C++容器插入元素的多个方法(附带实例)
C++ 使用容器时,在开始、结束或中间的某个位置插入新元素通常很有用。有一些算法,比如通过迭代器来向 range 插入元素,但是如果只传递迭代器,比如 begin() 返回的迭代器,它将不会插入元素而是覆盖容器的元素。此外,无法使用 end() 返回的迭代器在末尾插入元素。
为了执行这些操作,标准库提供了一组支持这些场景的迭代器和迭代器适配器。
1) 对于具有 push_back() 方法的容器,std::back_inserter() 用于在末尾插入元素:
2) 对于具有 push_front() 方法的容器,std::front_inserter() 用于在开头插入元素:
3) 对于具有 insert() 方法的容器,std::inserter() 用于在容器中的任意位置插入元素:
std::back_inserter()、std::front_inserter() 和 std::inserter() 都是辅助函数,用于创建 std::back_insert_iterator、std::front_insert_iterator 和 std::insert_iterator 类型的迭代器适配器。这些都是输出迭代器,用于向构造它们的容器中追加、前置或插入元素,递增和解引用这些迭代器没有任何作用。
然而,在赋值时,这些迭代器会从容器中调用以下方法:
以下是 std::back_insert_iterator 的过度简化的实现:
下面的示例在 std::vector 的开头插入 3 个值为 0 的元素:
std::inserter() 适配器接受两个参数:容器和应该插入元素的迭代器。在容器上调用 insert() 时,std::insert_iterator 会使迭代器加 1,因此在再次赋值时,它可以在下一个位置插入一个新元素。请看以下代码段:
这些迭代器适配器用于将多个元素插入 range 的算法或函数。当然,它们也可以用来插入单个元素,但这是一种反模式,因为在这种情况下,简单地调用 push_back()、push_front() 或 insert() 要简单得多,也直观得多。请考虑以下片段:
为了执行这些操作,标准库提供了一组支持这些场景的迭代器和迭代器适配器。
使用以下迭代器适配器在容器中插入新元素:本节中讨论的迭代器和迭代器适配器可在 <iterator> 头文件的 std 命名空间中找到。如果包含 <algorithm> 之类的头文件,则不必显式包含 <iterator>。
1) 对于具有 push_back() 方法的容器,std::back_inserter() 用于在末尾插入元素:
std::vector<int> v{ 1,2,3,4,5 }; std::fill_n(std::back_inserter(v), 3, 0); // v={1,2,3,4,5,0,0,0}
2) 对于具有 push_front() 方法的容器,std::front_inserter() 用于在开头插入元素:
std::list<int> l{ 1,2,3,4,5 }; std::fill_n(std::front_inserter(l), 3, 0); // l={0,0,0,1,2,3,4,5}
3) 对于具有 insert() 方法的容器,std::inserter() 用于在容器中的任意位置插入元素:
std::vector<int> v{ 1,2,3,4,5 }; std::fill_n(std::inserter(v, v.begin()), 3, 0); // v={0,0,0,1,2,3,4,5} std::list<int> l{ 1,2,3,4,5 }; auto it = l.begin(); std::advance(it, 3); std::fill_n(std::inserter(1, it), 3, 0); // l={1,2,2,3,0,0,0,4,5}
std::back_inserter()、std::front_inserter() 和 std::inserter() 都是辅助函数,用于创建 std::back_insert_iterator、std::front_insert_iterator 和 std::insert_iterator 类型的迭代器适配器。这些都是输出迭代器,用于向构造它们的容器中追加、前置或插入元素,递增和解引用这些迭代器没有任何作用。
然而,在赋值时,这些迭代器会从容器中调用以下方法:
- std::back_inserter 调用 push_back();
- std::front_inserter 调用 push_front();
- std::insert_iterator 调用 insert()。
以下是 std::back_insert_iterator 的过度简化的实现:
template<class C> class back_insert_iterator { public: typedef back_insert_iterator<C> T; typedef typename C::value_type V; explicit back_insert_iterator(C& c) : container(&c) { } T& operator=( const V& val ) { container->push_back( val ); return *this; } T& operator*() { return *this; } T& operator+++() { return *this; } T& operator+++( int ) { return *this; } protected: C* container; };由于赋值操作符的工作方式,这些迭代器只能用于某些标准容器:
- std::back_insert_iterator 可与 std::vector、std::list、std::deque 和 std::basic_string 一起使用;
- std::front_insert_iterator 可用于所有标准容器。
下面的示例在 std::vector 的开头插入 3 个值为 0 的元素:
std::vector<int> v{ 1,2,3,4,5 }; std::fill_n(std::inserter(v, v.begin()), 3, 0); // v={0,0,0,1,2,3,4,5}
std::inserter() 适配器接受两个参数:容器和应该插入元素的迭代器。在容器上调用 insert() 时,std::insert_iterator 会使迭代器加 1,因此在再次赋值时,它可以在下一个位置插入一个新元素。请看以下代码段:
T& operator=( const V& v) { iter = container->insert(iter, v); ++iter; return (*this); }这段代码(概念性地)展示了如何为 std::insert_iterator 适配器实现赋值操作符。可以看到,它首先调用容器的成员函数 insert(),然后对返回的迭代器进行递增。因为所有标准容器都有一个名为 insert() 的方法,该方法具有此签名,所以此适配器可用于所有这些容器。
这些迭代器适配器用于将多个元素插入 range 的算法或函数。当然,它们也可以用来插入单个元素,但这是一种反模式,因为在这种情况下,简单地调用 push_back()、push_front() 或 insert() 要简单得多,也直观得多。请考虑以下片段:
std::vector<int> v{ 1,2,3,4,5 }; *std::back_inserter(v) = 6; // v = {1,2,3,4,5,6} std::back_insert_iterator<std::vector<int>> it(v); *it = 7; // v = {1,2,3,4,5,6,7}这里所示的例子应该避免,它们不提供任何好处,只会让代码变得杂乱无章。