C++ premier重固之第九章:顺序容器

顺序容器内的元素按照其位置按照位置顺序存储和访问。

顺序容器有三种:vector、list、deque(双端队列,deck)。这三种容器的增、删、查、改的方式以及代价是不一样的,区别就在于此。

另外适配器adaptors(包括栈stack、队列queue和优先级队列priority_queue)用于配置新的操作接口,依据原始容器类型提供的操作,适应基础的容器类型。

相同的操作的接口相同。有些操作可以适用于所有容器,有些适用于关联容器或顺序容器,但是有些则适用于容器中的一部分子集,所以得看是什么操作。

vector适合随机快速访问,list适合快速插入和删除,deque是两端均可以插入和删除的双端队列,stack要FILO,queue需要FIFO,priority_queue适合优先级管理。

使用:

#include<vector>

#include<list>

#include<deque>

vector<data_type>  my_vector;

list<data_type>  my_list;

deque<data_type> my_deque;

解释一下:data_type就是你要操作的数据元素的类型,my_vector/my_list/my_deque现在都还是空的。所以的容器类型,各自定义了默认构造函数,用于创建指定类型的空容器对象。

容器构造函数
C<T>  c 创建一个名为c的空容器
C是容器名,
T是该容器存储的元素类型,
可以是int,string,double等,
c是容器对象的名字
适用于所有的容器
C c(c2) 创建一个c容器,并且c初始化为c2的副本 c和c2的容器类型以及存储元素的类型必须一致 适用于所有容器
C c(b,e) 创建一个c容器 c存储迭代器b和迭代器e范围之间的元素 适用于所有容器
C c(n,t) 用 n 个值为 t 的元素创建容器 c c的元素类型必须符合或者能转化为C的容器里的元素类型 适用于顺序容器
C c(n) 创建有n个初始化值的容器   适用于顺序容器
       

将一个容器初始化为另一个容器的副本:

C<T>  c;  

C1<T1> t(c);//将t初始化为c的一个副本,但是注意C<T>和C1<T1>必须一样,否则错误,

也就是说,将一个容器复制给另一个容器的时候,容器的类型和复制的元素的类型必须是一致的,否则会产生编译错误。

初始化为一段元素的副本:

C<T> c;

C1<T1> t(c.begin(), c.begin()+x);//这里x+c.begin()必须不大于c.end();否则就越界啦。

此时,因为使用的是迭代器,那么C<T>和C1<T1>可以不同,但是要兼容,这里使用了传递迭代器的方式进行初始化。

如果要复制子集的话,这个操作是很方便的。

vector<string>::iterator mid = svec.begin() + svec.size()/2; 
 // initialize front with first half of svec: The elements up to but not including *mid 
 deque<string> front(svec.begin(), mid); 
 // initialize back with second half of svec: The elements *mid through end of svec 
 deque<string> back(mid, svec.end()); 

其实,指针就是迭代器,那么容器可以用内置数组的指针来初始化:

char a[]={0,1,2,3,4,5,6,7,8,9};

分配和初始化指定数目的元素:

创建一个容器的时候,可以显式的指定容器的大小(可以是常量和非常量)和一个可选的元素初始化式。

const list<int>::size_type list_size = 64; 
list<string> slist(list_size, "eh?"); // 64 strings, each is eh? 
这段代码表示 slist 含有 64 个元素,每个元素都被初始化为“eh?”字符串。另外,可以只是指定容器的大小而不选择初始化,但是此时元素的类型必须是内置、复合类型,或者提供了默认构造函数的类型。提供容器大小的初始化,只是适用于顺序容器。

创建和初始化vector对象的四种方式:

第一种:vector<int> vec(10);

第二种:vector<int> vec(10,1);

第三种:int a[]={1,1,2,3,4,5,6,7,8,9};vector<int> vec(a,a+10);

第四种:vector<int> vec0(10,1);vector<int> vec(vec0);

复制容器对象的构造函数和使用两个迭代器的构造函数之间的差别:

复制容器构造函数复制另一个容器的全部元素,两个容器必须同类型;使用两个迭代器的构造函数可以将一个容器初始化为另一个容器的子序列,不要求两个容器是同类型的。

容器类型必须支持可以复制和赋值运算。引用类型不可以用作容器类型,此外还有auto_ptr和salse_iterm类型也不支持做容器类型。

容器的容器:

什么意思呢?就是容器的元素还是容器。比如:vector< vector<int> > vec; vector< vector<string> > lines;注意前面定义的时候是有两个空格的,为了和>>以及<<符号区分开来。

不可以使用容器来存储 iostream 对象: 容器的类型的元素操作必须有赋值和复制功能,而iostream不支持赋值和复制。

假设有一个名为 Foo 的类,这个类没有定义默认构造函数,但提供了需要一个 int 型参数的构造函数,定义一个存放 Foo 的 list 对象,该对象有 10 个元素:

list<Foo> ilist(10,1);

每种容器类型都提供若干共同工作的迭代器类型。与容器类型一样,所有迭代器具有相同的接口:如果某种迭代器支持某种操作,那么支持这种操作的其他迭代器也会以相同的方式支持这种操作。

 

常用迭代器运算
*iter 返回迭代器iter所指向元素的应用  
++iter和iter++ iter加1,指向容器的下一个元素  
--iter和iter-- 指向容器的上一个元素  
iter->mem 对iter进行解引用,获取指定元素中名为mem的成员,等效于(*iter).mem  
iter1==iter2或者
iter1!=iter2
比较两个迭代器是否相等(或不等).这里的相等,是第一指向的元素相等,但是如果两个都指向超出迭代器的位置的时候,他们也是相等的。  
     

vector 和 deque 容器的迭代器提供额外的运算:

iter+n和iter-n;<,>,<=,>=;

关系操作符(<,>,<=,>=)只适用于 vector 和 deque 容器,这是因为只有这种两种容器为其元素提供快速、随机的访问。它们确保可根据元素位置直接有效地访问指定的容器元素。这两种容器都支持通过元素位置实现的随机访问,因此它们的迭代器可以有效地实现算术和关系运算。 

list 容器的迭代器既不支持算术运算(加法或减法),也不支持关系运算(<=, <, >=, >),它只提供前置和后置的自增、自减运算以及相等(不等)运算。 所以,你不能这么做:list<int> l(vec.begin(),vec.end(); ilist.begin() + ilist.size()/2,是错误的。

编写一个循环将 list 容器的元素逆序输出:

list<int> ilist;
list<int>::iterator iter1=ilist.begin();
list<int>::iterator iter2=ilist.end();
while(iter1!=iter2)
         cout<<*(--iter2);//减一之后解引用,这里不能写为while(iter1<=iter2),
                          //<span style="font-family: Arial, Helvetica, sans-serif;">因为list的迭代器没有关系运算和算数运算,只有自增(减)相等不等运算</span>

迭代器范围:迭代器开始的位置和超出迭代器的下一个位置,有两种表示方法:begin和end,以及first和last。一般都是左闭合区间[firts, end).last只能最小等于first,不能指向first元素的前一个位置,不然就越界了。

当first等于last的时候,说明容器是空的,否则就是至少有一个元素。不过first自增到last的位置的时候,first==last,那么这就是退出循环的条件了。

使用迭代器编写程序时,必须留意哪些操作会使迭代器失效。使用无效迭代器将会导致严重的运行时错误。 

每种顺序容器都提供了一组有用的类型定义以及以下操作:增、删、设置大小、获取容器的第一个或者最后一个值。

三种由容器定义的类型:size_type、iterator 和 const_iterator。


容器定义的类型别名
iterator 此容器类型的迭代器类型        备注
size_type 无符号整型,足以存储此容器类型的最大可能容器长度   
const_iterator 只读迭代器容器类型  
reverse_iterator 按照逆序寻址的迭代器类型  
const_reverse_iterator 只读的逆序迭代器类型  
differrence_type 足够存储两个迭代器差值的有符号整型,可为负数  
value_type 元素类型  
reference 左值类型  
const_reference 常量左值  









int 型的 vector 容器应该使用什么类型的索引? 解答:vector<int>::iterator.

读取存放 string 对象的 list 容器时,应该使用什么类型?

 解答:list<string>::iterator和list<string>::const_iterator和list<string>::reverse_iterator和list<string>::const_reverse_iterator.

容器的  begin  和  end  操作:

c.begin();c.end();

c.rbegin();c.rend();//这个是逆序的时候用的。rbegin其实是最后一个元素。

在顺序容器中添加元素:push_back.这个操作,是所有的顺序容器都支持的。

while(cin>>text) 

c.push_back(text);

在容器中添加元素时,系统是将元素值复制到容器里。类似地,使用一段元素初始化新容器时,新容器存放的是原始元素的副本。被复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会受到影响,反之亦然。 

顺序容器中添加元素
c.push_back(t) 在容器的尾部添加元素t,返回值类型void 备注
c.push_front(t) 在容器的前端添加元素t,返回值类型void 适用于deque和list  
c.insert(p,t) 在迭代器 p 所指向的元素前面插入值为 t 的新元素。返回指向新添加元素的迭代器  
c.insert(p,n,t) 在迭代器 p 所指向的元素前面插入 n 个值为 t 的新元素。返回 void 类型  
c.insert(p,b,e) 在迭代器 p 所指向的元素前面插入由迭代器 b 和 e 标记的范围内的元素。返回 void 类型   
     
添加元素可能会使迭代器失效。
所有的容器类型都支持用关系操作符(第 5.2 节)来实现两个容器的比较。显比较的容器必须具有相同的容器类型,而且其元素类型也必须相同。例如,vector<int> 容器只能与 vector<int> 容器比较,而不能与 list<int> 或 vector<int> 容器比较,而不能与 list<int> 或 vector<double> 类型的容器比较。 容器的比较是基于容器内元素的比较。

C++ 语言只允许两个容器做其元素类型定义的关系运算。

顺序容器的大小操作:

c.size()  返回容器 c 中的元素个数。返回类型为 c::size_type 
c.max_size()  返回容器 c 可容纳的最多元素个数,返回类型为 c::size_type 

c.empty()  返回标记容器大小是否为 0 的布尔值 

c.resize(n)  调整容器 c 的长度大小,使其能容纳 n 个元素,如果 n < c.size(),则删除多出来的元素;否则,添加采用值初始化的新元素 。
c.resize(n,t)  调整容器 c 的长度大小,使其能容纳 n 个元素。所有新添加的元素值都为 t 。

容器类型提供 resize 操作来改变容器所包含的元素个数。如果当前的容器长度大于新的长度值,则该容器后部的元素会被删除;如果当前的容器长度小于新的长度值,则系统会在该容器后部添加新元素。

resize()操作可能使迭代器失效,这种情况就是当resize(n)中的n小于原来的容器的大小的时候。

访问顺序容器内元素的操作:

c.front()      返回容器的第一个元素的引用,如果容器为空,则操作未定义

c.back()     返回容器的最后一个元素的引用,如果容器为空,则操作未定义

c[n]             返回容器的下标为n的元素的引用,如果n<0或者n>c.size()则该操作未定义,只是适用于vector和deque容器

c.at(n)         返回下标为n的元素的引用,如果下标越界则该操作未定义。适用于vector和deque容器。

使用越界的下标,或调用空容器的 front 或 back 函数,都会导致程序出现严重的错误。但是可以用at()操作解决这个问题,因为at()操作会抛出一个异常。


删除元素

注:容器支持push_back(); push_front();pop_front(); pop_back();vector 容器类型不支持 pop_front 操作。

删除顺序容器内元素的操作:

c.erase(p)      删除迭代器p指向的元素,返回一个迭代器,它指向被删除元素后面的元素。如果 p 指向容器内的最后一个元素,则返回的迭代器指向容器的超出末端的下一位置。如果 p 本身就是指向超出末端的下一位置的迭代器,则该函数未定义 。

c.erase(b,e)   删除迭代器 b 和 e 所标记的范围内所有的元素 返回一个迭代器,它指向被删除元素段后面的元素。如果 e 本身就是指向超出末端的下一位置的迭代器,则返回的迭代器也指向容器的超出末端的下一位置 。

c.clear()         删除容器 c 内的所有元素。返回 void。

c.pop_back()  删除容器 c 的最后一个元素。返回 void。如果 c 为空容器,则该函数未定义 。

c.pop_front()  删除容器 c 的第一个元素。返回 void。如果 c 为空容器,则该函数未定义。只适用于 list 或 deque 容器。

erase()的操作必须是在找个这个元素的情况下,这个元素必须存在,否则没有什么意义,可以使用algorithm库中的find函数找元素的位置,如:

vector<int>::iterator   it=find(vec.begin() , vec.end() , search_value)。

删除容器中的所有的元素:

c.clear();或者c.erase(v.begin() , v.end());

erase、pop_front 和 pop_back 函数使指向被删除元素的所有迭代器失效。对于 vector 容器,指向删除点后面的元素的迭代器通常也会失效。而对于 deque 容器,如果删除时不包含第一个元素或最后一个元素,那么该 deque 容器相关的所有迭代器都会失效。


9.25    需要删除一段元素时,如果 val1 与 val2 相等,那么程序会发生什么事情?   会删不了。find函数找到的位置一样,然后erase(it,it)删不了。

            如果 val1 和 val2 中的一个不存在,或两个都不存在,程序又会怎么样?       会运行错误。


赋值与 swap
与赋值相关的操作符都作用于整个容器。除 swap 操作外,其他操作都可以用 erase 和 insert 操作实现(表 9.11)。赋值操作符首先 erases 其左操作数容器中的所有元素,然后将右操作数容器的所有元素 inserts 到左边容器中。


vector容器的自增长 
在容器对象中 insert 或压入一个元素时,该对象的大小增加 1。类似地,如果 resize 容器以扩充其容量,则必须在容器中添加额外的元素。

一般而言,使用 list 容器优于 vector 容器。但是,通常出现的反而是以下情况:对于大部分应用,使用 vector 容器是最好的。原因在于,标准库的实现者使用这样内存分配策略:以最小的代价连续存储元素。由此而带来的访问元素的便利弥补了其存储代价。 

vector容器的大小实际上比分配的能大些。


capacity 和 reserve 成员

vector 容器处理内存分配的细节是其实现的一部分。然而,该实现部分是由 vector 的接口支持的。vector 类提供了两个成员函数:capacity 和 reserve 使程序员可与 vector 容器内存分配的实现部分交互工作。capacity 操作获取在容器需要分配更多的存储空间之前能够存储的元素总数,而 reserve 操作则告诉 vector 容器应该预留多少个元素的存储空间。 弄清楚容器的 capacity(容量)与 size(长度)的区别非常重要。size 指容器当前拥有的元素个数;而 capacity 则指容器在必须分配新存储空间之前可以存储的元素总数。

一般来说,capacity比size要大,因为你懂得。如果使用的空间还没有完,那么vector就不在进行内存的分配。

每当 vector 容器不得不分配新的存储空间时,以加倍当前容量的分配策略实现重新分配。 
vector 的每种实现都可自由地选择自己的内存分配策略。然而,它们都必须提供 vector 和 capacity 函数,而且必须是到必要时才分配新的内存空间。分配多少内存取决于其实现方式。不同的库采用不同的策略实现。

容器的选用:连续的存储空间会影响内存分配策略和容器对象的开销。

插入操作和访问操作都可能影响容器的选用,比如插入操作的位置在头尾和中间对vector就是不一样的,对list可能没什么区别。

访问操作,如果要随机访问的话,vector反而好,但是list就不太好了。

选择容器类型的法则: 
1. 如果程序要求随机访问元素,则应使用 vector 或 deque 容器。 
2. 如果程序必须在容器的中间位置插入或删除元素,则应采用 list 容器。
3. 如果程序不是在容器的中间位置,而是在容器首部或尾部插入或删除元素,则应采用 deque 容器。 
4. 如果只需在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑在输入时将元素读入到一个 list 容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的 list 容器复制到一个 vector 容器。 

再谈 string 类型:

                                

                                

string 类型还支持大多数顺序容器操作。在某些方面,可将 string 类型视为字符容器。

string 支持的容器操作有: 
•  表 9.5 列出的 typedef,包括迭代器类型。 
•  表 9.2 列出的容器构造函数,但是不包括只需要一个长度参数的构造函数。 
•  表 9.7 列出的 vector 容器所提供的添加元素的操作。注意:无论 vector 容器还是 string 类型都不支持 push_front 操作。 
•  表 9.8 列出的长度操作。 
•  表 9.9 列出的下标和 at 操作;但 string 类型不提供该表列出的 back 和 front 操作。 
•  表 9.6 列出的 begin 和 end 操作。 
•  表 9.10 列出的 erase 和 clear 操作;但是 string 类型不入提供 pop_back 或 pop_front 操作。 
•  表 9.11 列出的赋值操作。 
•  与 vector 容器的元素一样,string 的字符也是连续存储的。因此,string 类型支持第 9.4 节描述的 capacity 和 reserve 操作。 

9.34 使用迭代器将 string 对象中的字符都改为大写字母:

#include<iostream>
#include<string>
#include<cctype>
using namespace std;
int main(){
	string str;
	string::iterator it=str.begin();
	while(it!=str.end()&&!str.empty())
		if(islower(*it)){
			toupper(*it);
			it++;
		}
		else
			it++;
		return 0;
}


9.35 使用迭代器寻找和删除 string 对象中所有的大写字符:

#include<iostream>
#include<string>
#include<cctype>
using namespace std;
int main(){
	string str;
	string::iterator it=str.begin();
	while(it!=str.end()&&!str.empty()){
		if(isupper(*it)){
			str.erase(*it);		
		}
		else
			it++;
<span style="white-space:pre">	</span>}<span style="white-space:pre">		</span>
		return 0;
}
 
 

9.36 编写程序用 vector<char> 容器初始化 string 对象:

const int N=100;
vector<char> vec(N,'s');
string str(vec.begin() , vec.end());

9.37 假设希望一次读取一个字符并写入 string 对象,而且已知需要读入至少 100 个字符,考虑应该如何提高程序的性能?

        你麻痹,老子不会。。。


与容器共有的 string 操作 
s.insert(p, t)  在迭代器 p 指向的元素之前插入一个值为 t 的新元素。返回指向新插入元素的迭代器 。

s.insert(p, n, t) 在迭代器 p 指向的元素之前插入 n 个值为 t 的新元素。返回 void s.insert(p, b, e) 在迭代器 p 指向的元素之前插入迭代器 b 和 e 标记范围内所有的元素。返回 void 。

s.assign(b, e)  在迭代器 b 和 e 标记范围内的元素替换 s。对于 string 类型,该操作返回 s;对于容器类型,则返回 void 。
s.assign(n, t)  用值为 t 的 n 个副本替换 s。对于 string 类型,该操作返回 s;对于容器类型,则返回 void 。
s.erase(p)  删除迭代器 p 指向的元素。返回一个迭代器,指向被删除元素后面的元素 。。
s.erase(b, e)  删除迭代器 b 和 e 标记范围内所有的元素。返回一个迭代器,指向被删除元素段后面的第一个元素 。。。


string 类型特有的版本 
s.insert(pos, n, c)  在下标为 pos 的元素之前插入 n 个字符 c 
s.insert(pos, s2)  在下标为 pos 的元素之前插入 string 对象 s2 的副本 
s.insert(pos, s2, pos2, len) 在下标为 pos 的元素之前插入 s2 中从下标 pos2 开 始的 len 个字符 
s.insert(pos, cp, len) 在下标为 pos 打元素之前插入 cp 所指向数组的前 len 个字符 
s.insert(pos, cp)  在下标为 pos 的元素之前插入 cp 所指向的以空字符结束的字符串副本 
s.assign(s2)  用 s2 的副本替换 s 
s.assign(s2, pos2, len) 用 s2 中从下标 pos2 开始的 len 个字符副本替换 s
s.assign(cp, len)  用 cp 所指向数组的前 len 个字符副本替换 s 
s.assign(cp)  用 cp 所指向的以空字符结束的字符串副本替换 s 450
s.erase(pos, len)  删除从下标 pos 开始的 len 个字符 除非特殊声明,上述所有操作都返回 s 的引用。‘

只适用于string的操作:

1,子串操作

子串操作 
s.substr(pos, n) 返回一个 string 类型的字符串,它包含 s 中从下标 pos 开始的 n 个字符 
s.substr(pos)     返回一个 string 类型的字符串,它包含从下标 pos 开始到s 末尾的所有字符 
s.substr()            返回 s 的副本 

修改 string 对象的操作(args 在表 9.18 中定义) 
s.append( args)  将 args 串接在 s 后面。返回 s 引用 
s.replace(pos, len, args) 删除 s 中从下标 pos 开始的 len 个字符,用 args 指定的字符替换之。返回 s 的引用 在这个版本中,args 不能为 b2,e2 
s.replace(b, e, args)  删除迭代器 b 和 e 标记范围内所有的字符,用 args 替换之。返回 s 的引用 在这个版本中,args 不能为 s2,pos2,len2 


 string查找操作符 :
s.find( args)  在 s 中查找 args 的第一次出现 
s.rfind( args)  在 s 中查找 args 的最后一次出现 
s.find_first_of( args)  在 s 中查找 args 的任意字符的第一次出现 
s.find_last_of( args)  在 s 中查找 args 的任意字符的最后一次出现
s.find_first_not_of( args) 在 s 中查找第一个不属于 args 的字符 
s.find_last_not_of( args)  在 s 中查找最后一个不属于 args 的字符 

string 类型提供的 find 操作的参数 
c, pos  在 s 中,从下标 pos 标记的位置开始,查找字符 c。pos 的默认值为 0 
s2, pos  在 s 中,从下标 pos 标记的位置开始,查找 string 对象 s2。pos 的默认值为 0 
cp, pos  在 s 中,从下标 pos 标记的位置形参,查找指针 cp 所指向的 C 风格的以空字符结束的字符串。pos 的默认值为 0 cp, pos, n 
在 s 中,从下标 pos 标记的位置开始,查找指针 cp 所指向数组的前 n 个字符。pos 和 n 都没有默认值


string 类型 compare 操作 
s.compare(s2)  比较 s 和 s2 
s.compare(pos1, n1, s2) 让 s 中从 pos 下标位置开始的 n1 个字符与 s2 做比较 
s.compare(pos1, n1, s2, pos2, n2) 让 s 中从 pos1 下标位置开始的 n1 个字符与 s2 中从 pos2  下标位置开始的 n2 个字符做比较 
s.compare(cp)  比较 s 和 cp 所指向的以空字符结束的字符串 
s.compare(pos1, n1, cp) 让 s 中从 pos1 下标位置开始的 n1 个字符与 cp 所指向的 字符串做比较 
s.compare(pos1, n1, cp, n2) 让 s 中从 pos1 下标位置开始的 n1 个字符与 cp 所指向的 字符串的前 n2 个字符做比较 


9.7. 容器适配器 

除了顺序容器,标准库还提供了三种顺序容器适配器:queue、priority_queue 和 stack。


stack 适配器
所关联的基础容器可以是任意一种顺序容器类型。因此,stack 栈可以建立在 vector、list 或者 deque 容器之上。而 queue 适配器要求其关联的基础容器必须提供 push_front 运算,因此只能建立在 list 容器上,而不能建立在 vector 容器上。priority_queue 适配器要求提供随机访问功能,因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器上。 


栈适配器 

栈容器适配器支持的操作 
s.empty()  如果栈为空,则返回 true,否则返回 stack
s.size()  返回栈中元素的个数 
s.pop()  删除栈顶元素的值,但不返回其值 
s.top()  返回栈顶元素的值,但不删除该元素 
s.push(item)  在栈顶压入新元素 

队列和优先级队列

队列和优先级队列支持的操作 
q.empty()  如果队列为空,则返回 true,否则返回 false 
q.size()  返回队列中元素的个数 
q.pop()  删除队首元素,但不返回其值 
q.front()  返回队首元素的值,但不删除该元素 该操作只适用于队列 
q.back()  返回队尾元素的值,但不删除该元素 该操作只适用于队列 
q.top()  返回具有最高优先级的元素值,但不删除该元素 该操作只适用于优先级队列 
q.push(item)  对于 queue,在队尾压入一个新元素,对于 priority_quue,在基于优先级的适当位置插入新元素


容器是用于存储某种给定类型对象的模板类型。在顺序容器中,所有元素根据其位置排列和访问。顺序容器共享一组通用的已标准化的接口:如果两种顺序容器都提供某一操作,那么该操作具有相同的接口和含义。所有容器都提供(有效的)动态内存管理。程序员在容器中添加元素时,不必操心元素存放在哪里。容器自己实现其存储管理。 

最经常使用的容器类型是 vector,它支持对元素的快速随机访问。可高效地在 vector 容器尾部添加和删除元素,而在其他任何位置上的插入或删除运算则要付出比较昂贵的代价。deque 类与 vector 相似,但它还支持在 deque 首部的快速插入和删除运算。list 类只支持元素的顺序访问,但在 list 内部任何位置插入和删除元素都非常快速。


在容器中添加或删除元素可能会使已存在的迭代器失效。当混合使用迭代器操作和容器操作时,必须时刻留意给定的容器操作是否会使迭代器失效。许多使一个迭代器失效的操作,例如 insert 或 erase,将返回一个新的迭代器,让程序员保留容器中的一个位置。使用改变容器长度的容器操作的循环必须非常小心其迭代器的使用。 



你可能感兴趣的:(C++ premier重固之第九章:顺序容器)