我们已经使用过三种容器定义的类型:size_type、iterator、const_iterator.所有容器都定义这三种类型,还定义了以下的类型。
===================================================================================================
在容器中添加元素;
在容器中删除元素;
设置容器的大小;
(如果有的话)获取容器内的第一个和最后一个元素。
(容器能进行哪些操作是要看是哪种容器类型——9.5节)
我们已经使用过三种容器定义的类型:size_type、iterator、const_iterator.所有容器都定义这三种类型,还定义了以下的类型。
表中最后三种类型使程序员无须直接知道容器元素的真正类型,就能使用它。需要使用元素类型时,只要用value_type 即可。
如果要引用该类型,则通过reference和const_reference 类型实现。(16章)
//使用容器定义类型的表达式看上去非常复杂
list<string>::iterator iter;
vector<int>::difference_type cnt;
上述每个操作都有不同版本:一个是const成员,另一个是非const成员。这些操作返回什么类型取决于容器是否为const;
如果容器不是const,则这些操作返回iterator或reverse_iterator类型。
如果容器是const,则返回类型要加上const_前缀,也就是const_iterator和const_reverse_iterator 类型。
//在容器中添加元素
string text_word;
while(cin>>text_word)
container.push_back(text_word);//调用push_back 函数会在容器container尾部创建一个新元素,并使容器的长度加1.新元素的值为text_word 对象的副本,而container的类型则可能是list、vector、deque。
除了push_back 运算,list和deque容器还提供了类似的操作:push_front 。这个操作实现在容器首部插入新元素的功能。
为什么vector容器不支持这个操作,因为vector容器元素是按顺序存储的,读取容器,但是添加和删除不方便( 9.5节)
而deque容器针对在首和尾部进行插入和删除操作会比较方便,在其他位置插入和删除操作使用list比较方便( 9.5节)
list<int> ilist;
for(size_t ix=0;ix!=4;ix++)
ilist.push_back(ix);//使用push_back操作在容器ilist尾部依次添加元素 0、1/2/3
for(size_t iy=0;iy!=4;iy++)
ilist.push_front(iy);//依次在ilist里面添加元素0,1,2,3
最后ilist内的元素序列为:3,2,1,0,0,1,2,3,
使用push_back 和push_front 操作可以非常方便地在容器的尾部或首部添加单个元素。
而insert 操作则提供了一组更加通用的插入方法,实现在容器的任意指定位置之前而非之后插入新元素。
insert 操作有三个版本,看上表。
vector<string> svec;
list<string> slist;
string spouse("Beth");
slist.insert(slist.begin(),spouse);
svec.insert(svec.begin(),spouse);
svec.insert(svec.end(),10,"Anana");//在svec的尾部插入10个元素,每个元素都是“Anana”
string sarray[4]={"asdsd","sdsds","sdfd","fdfdf"};
//可将数组中所有的或其中一部分元素插入到string类型的list容器中
slist.insert(slist.end(),sarray,sarray+4);
list<string>::iterator slist_iter = slist.begin();
slist.insert(slist_iter,sarray+2,sarray+4);
在vector容器中添加元素可能会导致整个容器的重新加载,这样的话,该容器涉及的所有迭代器都会失效。即使需要重新加载整个容器,指向新插入元素后面的那个元素的迭代器也会失效。
任何insert或push操作都可能导致迭代器失效,当编写循环将元素插入到vector或deque容器中时,程序必须确保迭代器在每次循环后都得到更新。
//下列程序错在哪里
vector<int>::iterator mid=iv.begin()+iter.size()/2;//事先定义一个迭代器
while(vector<int>::iterator iter!=mid)
if(iter==some_val)
iv.insert(iter,2*some_val);
//程序错误!!
//1. 对vector进行插入操作后会导致迭代器失效,而循环中使用了先前保存的迭代器mid(所以我们一般使用迭代器的时候,都是直接在for循环里面使用iter!=ivec.end(),而不是说事先就将ivec.end()赋值给某个变量)
//2. 循环中使用的迭代器iter没有初始化
//3. if语句中的条件应该是比较iter所指向的元素(即*iter)与some_val是否相等
//正确代码如下
vector<int>::iterator iter=iv.begin();
while(iter!=iv.begin()+iv.size()/2){//每次判断的时候都会重载下迭代器,重新计算
if(*iter==some_val)
{ iter=iv.insert(iter,2*some_val);//使用insert函数在迭代器所指元素的前面插入新元素,返回值为指向新插入元素的迭代器,所以将iter加上2才能指向下一个要处理的原始元素。
iter+=2;}//指向下一个要处理的元素
else
++iter;//指向下一个要处理的元素
}
在vector和deque容器中添加元素时,因为vector和queue插入元素都要移动元素,可能会导致某些或全部迭代器失效。(list不需要移动元素,因为list非顺序存取)假设所有迭代器失效是最安全的做法,这个建议特别适用于end操作返回的迭代器。在容器的任何位置插入任何元素都会使该迭代器失效。
例如,考虑一个读取容器中每个元素的循环,对读出元素做完处理后,在原始元素后面插入一个新元素。我们希望该循环可以处理每个原始元素,然后使用insert函数插入新元素,并返回指向刚插入元素的迭代器。在每次插入操作完成后,给返回的迭代器自增1,以使循环定位在下一个要处理的原始元素。我们尝试通过存储end操作返回的迭代器来“优化”该循环,将导致灾难性错误:
vector<int>::iterator first=v.begin(),
last=v.end();//事先定义了指向end的迭代器,会导致错误!!
while(first!=last){
first=v.insert(first,42);
++first;
//该代码将导致死循环,问题在于这个程序将end操作返回的迭代器值存储在名为last的局部变量中。循环体中实现了元素的添加运算,添加元素会使得存储在last中的迭代器失效。该迭代器既没有指向容器v的元素,也不再指向v的超出末端的下一个位置。
}
while(first!=v.end()){
first=v.insert(first,42);
++first;
}