三种容器均支持 resieze() 操作,重新划定容器大小,且此函数有重载。
vector : vector和built-in数组类似,是一个在堆上建立的一维数组,它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符。vector因为存储在堆上,所以支持erase(
), resieze()(重新划分容器容量)等操作; vector不用担心越界当空间不够用的时候,系统会自动按照一定的比例(对capacity( )大小)进行扩充。在vector序列末尾添加(push_back( ))或者删除(pop_back(
))对象效率高,在中间进行插入或删除效率很低,主要是要进行元素的移动和内存的拷贝,原因就在于当内存不够用的时候要执行重新分配内存,拷贝对象到新存储区,销毁old对象,释放内存等操作,如果对象很多的话,这种操作代价是相当高的。为了减少这种代价,使用vector最理想的情况就是事先知道所要装入的对象数目,用成员函式 reserve( )预定下来;vector最大的优点莫过于是检索(用operator[])速度在这三个容器中是最快的。
list : list的本质是一个双向链表(根据sgistl源代码),内存空间不连续,通过指针进行操作。说道链表,它的高效率首先表现是插入,删除元素,进行排序等等需要移动大量元素的操作。显然链表没有检索操作operator[ ], 也就是说不能对链表进行随机访问,而只能从头至尾地遍历,这是它的一个缺陷。list有不同于前两者的某些成员方法,如合并list的方法splice( ), 排序sort( ),交换list 的方法swap( )等等。
deque :deque是一个double-ended。queue是由多个连续内存块构成,deque是list和vector的兼容,分为多个块,每一个块大小是512字节,块通过map块管理,map块里保存每个块得首地址。因此该容器也有索引操作operator[ ],效率没vector高。另外,deque比vector多了push_front() & pop_front( )操作。在两端进行此操作时与list的效率 差不多。
表 9.2. 容器构造函数 | |
C<T> c; | 创建一个名为 c 的空容器。C 是容器类型名,如 vector,T 是元素类型,如 int 或 string 适用于所有容器。 |
C c(c2); | 创建容器 c2 的副本 c;c 和 c2 必须具有相同的容器类型,并存放相同类型的元素。适用于所有容器。 |
C c(b, e); | 创建 c,其元素是迭代器 b 和 e 标示的范围内元素的副本。适用于所有容器。 |
C c(n, t); | 用 n 个值为 t 的元素创建容器 c,其中值 t 必须是容器类型 C 的元素类型的值,或者是可转换为该类型的值。 只适用于顺序容器 |
C c(n); | 创建有 n 个值初始化(第 3.3.1 节)(value-initialized)元素的容器 c。 只适用于顺序容器 |
表 9.3. 常用迭代器运算 | |
*iter | 返回迭代器 iter 所指向的元素的引用 |
iter->mem | 对 iter 进行解引用,获取指定元素中名为 mem 的成员。等效于 (*iter).mem |
++iter iter++ | 给 iter 加 1,使其指向容器里的下一个元素 |
--iter iter-- | 给 iter 减 1,使其指向容器里的前一个元素 |
iter1 == iter2 | 比较两个迭代器是否相等(或不等)。当两个迭代器指向同一个容器中的同一个元素,或者当它们都指向同一个容器的超出末端的下一位置时,两个迭代器相等 |
表 9.4. vector 和 deque 类型迭代器支持的操作 | |
iter + n | 在迭代器上加(减)整数值 n,将产生指向容器中前面(后面)第 n 个元素的迭代器。新计算出来的迭代器必须指向容器中的元素或超出容器末端的下一位置 |
iter1 += iter2 | 这里迭代器加减法的复合赋值运算:将 iter1 加上或减去 iter2 的运算结果赋给 iter1 |
iter1 - iter2 | 两个迭代器的减法,其运算结果加上右边的迭代器即得左边的迭代器。这两个迭代器必须指向同一个容器中的元素或超出容器末端的下一位置 只适用于 vector 和 deque 容器 |
>, >=, <, <= | 迭代器的关系操作符。当一个迭代器指向的元素在容器中位于另一个迭代器指向的元素之前,则前一个迭代器小于后一个迭代器。关系操作符的两个迭代器必须指向同一个容器中的元素或超出容器末端的下一位置 只适用于 vector 和 deque 容器 |
关系操作符只适用于 vector 和 deque 容器,这是因为只有这种两种容器为其元素提供快速、随机的访问。
代码:
// copyelements from vec into ilist
list<int> ilist(vec.begin(), vec.end());
ilist.begin()+ ilist.size()/2; // error: no addition on list iterators
是错误的。list 容器的迭代器既不支持算术运算(加法或减法),也不支持关系运算(<=, <, >=, >),它只提供前置和后置的自增、自减运算以及相等(不等)运算。
表 9.5. 容器定义的类型别名
size_type | 无符号整型,足以存储此容器类型的最大可能容器长度 |
iterator | 此容器类型的迭代器类型 |
const_iterator | 元素的只读迭代器类型 |
reverse_iterator | 按逆序寻址元素的迭代器 |
const_reverse_iterator | 元素的只读(不能写)逆序迭代器 |
difference_type | 足够存储两个迭代器差值的有符号整型,可为负数 |
value_type | 元素类型 |
reference | 元素的左值类型,是 value_type& 的同义词 |
const_reference | 元素的常量左值类型,等效于 const value_type& |
表 9.6. 容器的begin 和end 操作
c.begin() | 返回一个迭代器,它指向容器 c 的第一个元素 |
c.end() | 返回一个迭代器,它指向容器 c 的最后一个元素的下一位置 |
c.rbegin() | 返回一个逆序迭代器,它指向容器 c 的最后一个元素 |
c.rend() | 返回一个逆序迭代器,它指向容器 c 的第一个元素前面的位置 |
关键概念:容器元素都是副本
表 9.7 在顺序容器中添加元素的操作
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 类型 |
不要存储 end 操作返回的迭代器。添加或删除 deque 或 vector 容器内的元素都会导致存储的迭代器失效。 |
为了避免存储 end 迭代器,可以在每次做完插入运算后重新计算end 迭代器值:
// safer: recalculate end on each trip whenever the loop adds/erases elements
while (first != v.end()) {
// do some processing
first = v.insert(first, 42); // insert new value
++first; // advance first just past the element we added
}
表 9.8. 顺序容器的大小操作
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 |
表 9.9. 访问顺序容器内元素的操作
c.back() | 返回容器 c 的最后一个元素的引用。如果 c 为空,则该操作未定义 |
c.front() | 返回容器 c 的第一个元素的引用。如果 c 为空,则该操作未定义 |
c[n] | 返回下标为 n 的元素的引用 如果 n <0 或 n >= c.size(),则该操作未定义 只适用于 vector 和 deque 容器 |
c.at(n) | 返回下标为 n 的元素的引用。如果下标越界,则该操作未定义 只适用于 vector 和 deque 容器 |
// check that there are elements before dereferencing an iterator
// or calling front or back
if (!ilist.empty()) {
// val and val2 refer to the same element
list<int>::reference val = *ilist.begin();
list<int>::reference val2 = ilist.front();
// last and last2 refer to the same element
list<int>::reference last = *--ilist.end();
list<int>::reference last2 = ilist.back(); }
表 9.10. 删除顺序容器内元素的操作
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 容器 |
删除容器内所有元素
要删除容器内所有的元素,可以调用 clear 函数,或将 begin 和 end 迭代器传递给erase 函数。
slist.clear(); // delete all the elements within the container
slist.erase(slist.begin(), slist.end()); // equivalent
erase、pop_front 和 pop_back 函数使指向被删除元素的所有迭代器失效。对于 vector 容器,指向删除点后面的元素的迭代器通常也会失效。而对于 deque 容器,如果删除时不包含第一个元素或最后一个元素,那么该 deque 容器相关的所有迭代器都会失效。 |
表 9.11. 顺序容器的赋值操作
c1 = c2 | 删除容器 c1 的所有元素,然后将 c2 的元素复制给 c1。c1 和 c2 的类型(包括容器类型和元素类型)必须相同 |
c1.swap(c2) | 交换内容:调用完该函数后,c1 中存放的是 c2 原来的元素,c2 中存放的则是 c1 原来的元素。c1 和 c2 的类型必须相同。该函数的执行速度通常要比将 c2 复制到 c1 的操作快 |
c.assign(b,e) | 重新设置 c 的元素:将迭代器 b 和 e 标记的范围内所有的元素复制到 c 中。b 和 e 必须不是指向 c 中元素的迭代器 |
c.assign(n,t) | 将容器 c 重新设置为存储 n 个值为 t 的元素 |
选择容器的提示
下面列举了一些选择容器类型的法则:
1. 如果程序要求随机访问元素,则应使用vector 或 deque 容器。
2. 如果程序必须在容器的中间位置插入或删除元素,则应采用list 容器。
3. 如果程序不是在容器的中间位置,而是在容器首部或尾部插入或删除元素,则应采用deque 容器。
4. 如果只需在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑在输入时将元素读入到一个list 容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的list 容器复制到一个 vector 容器。
#include <iostream>
#include <vector>
#include <list>
#include <string> //不加头文件error C2679: 二进制“<<”: 没有找到接受“std::string”类型的右操作数的运算符(或没有可接受的转换)
#include "sales_item.h"
using namespace std;
int main( int argc , char *argv[]){
vector<int> ivec;
list<int>::iterator iter;
list<string>::iterator iter1;
//list<string>::const_iterator iter1; //注意这里要用const,指向const对象
ivec.push_back(1);
ivec.push_back(2);
/*
不能直接将一种容器内的元素复制给另一种容器,但系统允许通过传递一对迭代器(第 3.4 节)
间接实现该实现该功能。使用迭代器时,不要求容器类型相同。容器内的元素类型也可以不相同,
只要它们相互兼容,能够将要复制的元素转换为所构建的新容器的元素类型,即可实现复制。
*/
list<int> ilist( ivec.begin() , ivec.end() );
//for( iter = ilist.begin() ; iter != ilist.end() ;++iter ){
// cout << *iter << " ";
//}
//cout << endl;
/************************************************************************/
/* 指针就是迭代器,因此允许通过使用内置数组中的一对指针初始化容器 */
/************************************************************************/
char *words[] = {"stately", "plump", "buck", "mulligan"};
cout << sizeof(words) << endl;
// calculate how many elements in words
size_t words_size = sizeof(words)/sizeof(char *);
// use entire array to initialize words2
/* 使用 sizeof(5.8 节)计算数组的长度。将数组长度加到指向第一个元素
的指针上就可以得到指向超出数组末端的下一位置的指针。*/
list<string> words2( words ,words + words_size );
/* 通过指向第一个元素的指针 words 和指向数组中最后一个元素的下一位置
的指针,实现了 words2 的初始化。其中第二个指针提供停止复制的条件,
其所指向的位置上存放的元素并没有复制。
*/
//list<string> words2( words + words_size ,words); //error
/* 注意 != 适合所有顺序容器 ,但 < > <= >= 只适合vector和 deque */
//for( iter1 = words2.begin() ; iter1 != words2.end() ;++iter1 ){
// cout << *iter1 << "---";
//}
//cout << endl;
/*
假设有一个名为 Foo 的类,这个类没有定义默认构造函数,但提供了需要一个
int 型参数的构造函数,定义一个存放 Foo 的 list 对象,该对象有 10 个元素。
*/
//list< Foo > foo_list(10 , 1 ) ;
/* insert学习 */
list< string > str_lst;
list< string >::iterator str_lst_iter;
str_lst_iter = str_lst.begin();
str_lst.insert( str_lst_iter , 10 , "Anna" );
str_lst.insert( str_lst_iter , "Babu");
for( str_lst_iter = str_lst.begin() ; str_lst_iter != str_lst.end() ;++str_lst_iter ){
cout << *str_lst_iter << "---"; //9个Anna+Babu
}
cout << endl;
/* resize 学习 */
list<int> ilist1(10, 42); // 10 ints: each has value 42
cout << "list size : " << ilist1.size() <<endl; //10
ilist1.resize(15); // adds 5 elements of value 0 to back of ilist
cout << "list size : " << ilist1.size() <<endl; //15
ilist1.resize(25, -1); // adds 10 elements of value -1 to back of ilist
cout << "list size : " << ilist1.size() <<endl; //25
ilist1.resize(5); // erases 20 elements from the back of ilist
cout << "list size : " << ilist1.size() <<endl; //5
/* erase 学习 */
iter1 = words2.begin();
words2.erase( iter1 );
for( iter1 = words2.begin() ; iter1 != words2.end() ;++iter1 ){
cout << *iter1 << "---"; //9个Anna+Babu
}
return 0;
}