添加元素相关操作:
--------------------------------------------------------------------------------------------------------
c.push_back(t) 在容器 c 的尾部添加值为 t 的元素。返回 void 类型
c.push_front(t) 在容器 c 的前端添加值为 t 的元素。返回 void 类型
只适用于 list 和 deque 容器类型.
c.insert(p,t) 在迭代器 p 所指向的元素前面插入值为 t 的新元素。返回
指向新添加元素的迭代器
c.insert(p,n,t) 在迭代器 p 所指向的元素前面插入 n 个值为 t 的新元素。
返回 void 类型
c.insert(p,b,e) 在迭代器 p 所指向的元素前面插入由迭代器 b 和 e 标记
的范围内的元素。返回 void 类型
--------------------------------------------------------------------------------------------------------
在容器中指定位置添加元素
使用 push_back 和 push_front 操作可以非常方便地在顺序容器的尾部或首
部添加单个元素。而 insert 操作则提供了一组更通用的插入方法,实现在容器的
任意指定位置插入新元素。insert 操作有三个版本(表 9.7)。第一个版本需要一
个迭代器和一个元素值参数,迭代器指向插入新元素的位置。下面的程序就是使
用了这个版本的 insert 函数在容器首部插入新元素:
vector<string> svec;
list<string> slist;
string spouse("Beth");
// equivalent to calling slist.push_front (spouse);
slist.insert(slist.begin(), spouse);
// no push_front on vector but we can insert before begin()
// warning: inserting anywhere but at the end of a vector is an
expensive operation
svec.insert(svec.begin(), spouse);
新元素是插入在迭代器指向的位置之前。迭代器可以指向容器的任意位置,包
括超出末端的下一位置。由于迭代器可能指向超出容器末端的下一位置,这是
一个不存在的元素,因此 insert 函数是在其指向位置之前而非其后插入元素。
代码
slist.insert(iter, spouse); // insert spouse just before iter
就在 iter 指向的元素前面插入 spouse 的副本。
这个版本的 insert 函数返回指向新插入元素的迭代器。可使用该返回值在
容器中的指定位置重复插入元素:
list<string> lst;
list<string>::iterator iter = lst.begin();
while (cin >> word)
iter = lst.insert(iter, word); // same as calling push_front
要彻底地理解上述循环是如何执行的,这一点非常重要——特
别是要明白我们为什么说上述循环等效于调用 push_front 函
数。
循环前,将 iter 初始化为 lst.begin()。此时,由于该 list 对象是空的,
因此 lst.begin() 与 lst.end() 相等,于是 iter 指向该(空)容器的超出末
端的下一位置。第一次调用 insert 函数时,将刚读入的元素插入到 iter 所指
向位置的前面,容器 lst 得到第一个也是唯一的元素。然后 insert 函数返回
指向这个新元素的迭代器,并赋给 iter,接着重复 while 循环,读入下一个单
词。只要有单词要插入,每次 while 循环都将新元素插入到 iter 前面,然后
重置 iter 指向新插入元素。新插入的元素总是容器中的第一个元素,因此,每
次迭代器都将元素插入在该 list 对象的第一元素前面。
插入一段元素
insert 函数的第二个版本提供在指定位置插入指定数量的相同元素的功能:
svec.insert(svec.end(), 10, "Anna");
上述代码在容器 svec 的尾部插入 10 个元素,每个新元素都初始化为 "Anna"。
insert 函数的最后一个版本实现在容器中插入由一对迭代器标记的一段范
围内的元素。例如,给出以下 string 类型的数组:
string sarray[4] = {"quasi", "simba", "frollo", "scar"};
可将该数组中所有的或其中一部分元素插入到 string 类型的 list 容器中:
// insert all the elements in sarray at end of slist
slist.insert(slist.end(), sarray, sarray+4);
list<string>::iterator slist_iter = slist.begin();
// insert last two elements of sarray before slist_iter
slist.insert(slist_iter, sarray+2, sarray+4);
添加元素可能会使迭代器失效
正如我们在第 9.4 节中了解的一样,在 vector 容器中添加元素可能会导
致整个容器的重新加载,这样的话,该容器涉及的所有迭代器都会失效。即使需
要重新加载整个容器,指向新插入元素后面的那个元素的迭代器也会失效。
任何 insert 或 push 操作都可能导致迭代器失效。当编写循
环将元素插入到 vector 或 deque 容器中时,程序必须确保迭
代器在每次循环后都得到更新。
424
避免存储end 操作返回的迭代器
在 vector 或 deque 容器中添加元素时,可能会导致某些或全部迭代器失
效。假设所有迭代器失效是最安全的做法。这个建议特别适用于由 end 操作返
回的迭代器。在容器的任何位置插入任何元素都会使该迭代器失效。
例如,考虑一个读取容器中每个元素的循环,对读出元素做完处理后,在原
始元素后面插入一个新元素。我们希望该循环可以处理每个原始元素,然后使用
insert 函数插入新元素,并返回指向刚插入元素的迭代器。在每次插入操作完
成后,给返回的迭代器自增 1,以使循环定位在下一个要处理的原始元素。如果
我们尝试通过存储 end() 操作返回的迭代器来“优化”该循环,将导致灾难性
错误:
vector<int>::iterator first = v.begin(),
last = v.end(); // cache end iterator
// diaster: behavior of this loop is undefined
while (first != last) {
// do some processing
// insert new value and reassign first, which otherwise would
be invalid
first = v.insert(first, 42);
++first; // advance first just past the element we added
}
上述代码的行为未定义。在很多实现中,该段代码将导致死循环。问题在于
这个程序将 end 操作返回的迭代器值存储在名为 last 的局部变量中。循环体
中实现了元素的添加运算,添加元素会使得存储在 last 中的迭代器失效。该迭
代器既没有指向容器 v 的元素,也不再指向 v 的超出末端的下一位置。
不要存储 end 操作返回的迭代器。添加或删除 deque 或
vector 容器内的元素都会导致存储的迭代器失效。
为了避免存储 end 迭代器,可以在每次做完插入运算后重新计算 end 迭代器
值:
// safer: recalculate end on each trip whenever the loop adds/erases
elements
while (first != v.end()) {
// do some processing
425
first = v.insert(first, 42); // insert new value
++first; // advance first just past the element we added
}