关于C++中的顺序容器

本文参考了http://www.cnblogs.com/yongbufangqi1988/archive/2010/07/29/1788204.html
1、顺序容器包括:vector,list,deque
2、接受容器大小做形参的构造函数只适用于顺序容器,而关联容器不支持这种初始化。
3、作为容器元素类型必须满足以下两个约束:
 一、元素类型必须支持赋值运算。
 二、元素类型的对象必须可以复制。
上面的两条是最低限度的元素类型约束。除了引用类型外,所有内置或复合类型都可用做元素类型。
4、只有vector和deque容器提供以下两种重要的运算集合:
一、迭代器算术运算:iter+n
  iter-n
  iter1+=iter2
 iter1-=iter2
iter1-iter2


 二、除了==和!=之外的关系操作符来比较两个迭代器:>、>=、<、<=
5、关于添加,插入或删除元素可能会使迭代器失效
关于这个问题首先要弄清楚vector,list,deque在内存中是怎样存储的:
vector容器的元素以连续的方式存放——每一个元素都紧挨着前一个元素存储。所以当容器中已经没有空间容纳新的元素,vector必须重新分配存储空间,用来存放原来的元素以及新添加的元素。在vector中某个位置插入一个元素时,该位置后面的元素都要向后移动了。删除某个元素道理与插入一样。
list容器表示不连续的内存区域,list存储类似于链表,故插入或删除list容器中的一个元素不需要移动任何其他元素。

下面我给出两端代码来证明vector和list的存储方式:

#include<iostream>
#include <vector>
#include <list>
using namespace std;
int main()
{
vector<int> vec;
vec.push_back(3);
vector<int>::iterator ite=vec.begin();
cout<<"第一个元素的内容是"<<*ite<<endl;
cout<<"起始地址是"<<&vec[0]<<endl;
for(int i=0;i<100000;i++)
vec.push_back(3);
cout<<"第一个元素的内容是"<<*ite<<endl;
cout<<"起始地址是"<<&vec[0]<<endl;
list<int>li;
li.push_back(3);
list<int>::iterator it=li.begin();
cout<<"第一个元素 的内容是"<<*it<<endl;
for(int i=0;i<100000;i++)
li.push_back(3);
cout<<"第一个元素的内容是"<<*it<<endl;
getchar();
}

运行结果是:


 有运行结果可以看出vec起初的起始地址是0x310f18,通过插入10000元素后,起始地址变成了0x320020,说明vector起始的容量已经不能满足再添加10000的要求了,故必须重新开辟一块内存空间来存放元素。同时该程序也说明:vector容器添加元素会使迭代器失效。因为迭代器也是指针,当vector开辟新内存空间来存放更多的元素时候,原来的内存空间就要被收回了,这时候指向以前内存空间的迭代器就成了“野迭代器”了!而list容器迭代器经过增加元素后并没有失效,这也从侧面说明list容器在添加元素(不论添加多少个元素)都不需要移动任何其他元素。
6、容器之间的比较是基于容器内元素的比较。它的比较方法类似于string类型的比较。
7、front和back   pk    begin和end
  front将返回容器内第一个元素的引用,返回类型是size_type;back将返回容器最后一个元素的引用,返回类型也是size_type
  begin将返回指向容器内第一元素的迭代器,返回类型是iterator;end将返回容器最后一个元素的下一个位置的迭代器,返回类型是iterator
8、顺序容器中能够使用下表操作的有vector和deque,但是要注意:容器的下表只能用于获取已存在的元素,不能用来添加元素。这两天老是犯这个错误,可能数组毒害太大了!
9、赋值pk swap

赋值操作符首先删除其左操作数容器中的所有元素,然后将右操作数容器的所有元素插入到左边容器中:
c1=c2等价于c1.erase(c1.begin(),c2.end());c1.insert(c1.begin(),c2.begin(),c2.end());
c1.swap(c2)作用是交换内容:调用完该函数后,c1中存放的是c2原来的元素,c2中存放的则是c1原来的元素。
c++primer上有这样一段话:该操作不会删除或插入任何元素,而且保证在常量时间内实现交换。由于容器内没有移动任何元素,因此迭代器不会失效。读完就会想:为什么呢?为什么没有元素移动?为什么迭代器不会失效?我当时猜想应该是:该操作只是把分给两个容器的内存的地址进行交换,来实现内容交换!下面我写了代码验证这一想法

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> vec1;
    vec1.resize(3,2);
    vector<int> vec2;
    vec2.resize(4,4);
    cout<<"交换前两个容器中存储的内存地址:"<<endl;
    cout<<"vec1中存储的内存块的地址是:"<<&vec1[0]<<endl;
    cout<<"vec2中存储的内存块的地址是:"<<&vec2[0]<<endl;
    vec1.swap(vec2);
     cout<<"交换后两个容器中存储的内存地址:"<<endl;
    cout<<"vec1中存储的内存块的地址是:"<<&vec1[0]<<endl;
    cout<<"vec2中存储的内存块的地址是:"<<&vec2[0]<<endl;
    getchar();
}


运行结果是:
<img src='https://img-my.csdn.net/uploads/201206/05/1338902774_6226.jpg' /><img src='https://img-my.csdn.net/uploads/201206/05/1338902774_6226.jpg' /><img src='https://img-my.csdn.net/uploads/201206/05/1338902774_6226.jpg' /><img src='https://img-my.csdn.net/uploads/201206/05/1338902774_6226.jpg' />


这样明白了吧:swap交换的不是两个容器中的内容而是两个容器中存储的地址!所以这样交换前的迭代器也不会失效了!


再贴一个程序实例:
#include <iostream>
#include <set>
#include <vector>
#include <list>


int main()
 // vector 
std::vector<int> myvector;


 // 一个 vector 就像一个动态的一维数组19楼空间'qe7mEi2fa@
 // 声明一个存放 100 个 0 的整数的 vector
// 返回 vector 的大小, 即元素的个数
 std::cout << myvec.size() << std::endl;
 // 删除 vector 末尾的元素, vector 的大小相应减1
 myvec.pop_back();


 // 在 vector 的末尾添加元素
 myvec.push_back(10);


 // 返回 vector 末尾的元素
 std::cout << myvec.back() << std::endl;


 // 清除 vector 所有元素, vector 的个数为 0
 myvec.clear();
std::cout << myvec.size() << std::endl;
 // std::cout << myvec[1] << std::endl;    //这种方法访问越界会异常
 // std::cout << myvec.at(5) << std::endl; //这种方法访问越界会异常


 // 遍历
// 遍历方法1, 推荐
 for(std::vector<int>::iterator iter_1 = myvec.begin(); iter_1 != myvec.end(); iter_1++)
  std::cout << (int)*iter_1 << std::endl;


 // 遍历方法2, 不推荐, 原因忘记
 for(int i = 0; i <= myvec.size(); i++)
std::cout << myvec[i] << std::endl;
 // set
 // set 是集合, 但 set中不能包含重复的元素, 这是与 vector 的区别


 std::set<int> myset;


 myset.insert(10); // 插入元素19楼空间W1RN j3a
 myset.insert(10); // 没用, 插入失败, 因为不能有重复元素
 myset.erase(10);  // 删除元素,如果元素存在的话
myset.size();
 myset.clear();
 // list 相当于一个列表
 std::list<int> mylist;
 mylist.push_back(100);
 mylist.remove(100);


 /*
  *   vector用的是随机存取迭代器,list用的是双向迭代器
  *   
*   如果我们需要随机访问一个容器则vector   要比list   好得多  
*   如果我们已知要存储元素的个数则vector   又是一个比list   好的选择   
 *   如果我们需要的不只是在容器两端插入和删除元素则list   显然要比vector   好   
     *   除非我们需要在容器首部插入和删除元素否则vector   要比deque   好
8]U;k z*c;R{0  */


 return 0;

几种顺序容器的有缺点:
    vector 就是一头开口的队列,所以就是一般意义上的队列。
    deque 是两头开口的。既能往前插也能往后插。
    但是它两都一样,如果非要往中间插,就得腾地。vector 没办法,不管往哪插都得往后挪,所以你往第一个插最惨,整个序列都要复制。deque 好一点,如果你插入的位置比较靠前,它会想办法把前面的部分往前挪来腾地。所以最惨的是往中间插。
但是它们的访问效率都很高,vector 只要用首地址,加偏移量就可以直接找到要访问的元素。deque 稍微复杂,但总的来说,访问的位置也是直接算出来的,所以它们的访问时间都是常数 O(1)。


list 和它们思路不一样。你可以自由的在任何位置插入,插入时间是常数。但访问很慢,它根本不知道你要找的元素在哪里,只能从头遍历。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值