关于容器

来源:互联网 发布:sqlserver 物化视图 编辑:程序博客网 时间:2024/05/16 09:53

  • vector的capacity和size的区别:size是指它已经保存元素的个数,capacity是指在不分配内存空间的前提下它最多可以保存的元素;
  • vector中的元素在内存中是连续存放的,所以需要capacity,list没有capacity是因为其存储元素的内存是不连续的,不需要capacity,array是因为其大小是固定的。

    顺序容器类型:
  • vector: 可变大小数组,直吹快速随机访问,在尾部之外的位置插入或者删除元素可能会很慢;
  • deque: 双端队列。支持快速随机访问,在头尾插入删除速度很快;
  • list: 双向链表。只支持双向顺序访问,在list中任何位置进行插入删除操作速度都很快;
  • forward_list: 单向链表。只支持单向顺序访问。在链表任何位置进行插入删除操作速度都很快;
  • array: 固定大小数组。支持快速随机访问,不能添加删除元素。
  • string: 与vector相似的容器,但是专门用于保存字符,随机访问快,在尾部插入删除速度快。

    选择容器原则:
  • 除非有很好的理由选择其他容器,否则使用vector;
  • 如果程序有很多小的元素,且空间的额外开销很重要,则不要使用list或者forward_list;
  • 如果程序要求随机访问元素,应该使用vector或者deque;
  • 如果程序要求在容器的中间插入或者删除元素,应使用list或者forward_list;
  • 如果程序需要在头尾位置插入或者删除元素,但是不会在中间位置插入删除,使用deque;
  • 如果只有 在读取输入时才需要在容器中间位置插入元素,随后需要随机访问元素,则:
    首先,确定是否真需要在容器中间位置添加元素。当处理数据时,通常可以很容易的向vector追加数据,然后调用sort函数,重拍容器元素,来避免在中间位置添加元素。
    如果必须在中间位置插入元素,考虑在输入阶段使用list,一旦输入完成,将list中的内容拷贝到一个vector中。
    如果程序既需要随机访问元素,又需要在容器中间位置插入元素,答案取决于在list或者forward_list中访问元素与vector或者deque中插入删除元素的相对性能。

容器操作可能使迭代器失效

向容器中添加元素和从容器中删除元素的操作可能会使指向容器元素的指针、引用或迭代器失效。一个失效的指针、引用或迭代器将不再表示任何与安生。

在向容器添加元素后:

  1. 如果容器是vector或string,且存储空间被重新分配,则指向容器的迭代器、指针和引用都会失效。如果存储空间未重新分配,指向插入位置之前的元素的迭代器、指针和引仍有效,但指向插入位置之后的任何位置都会导致迭代器、指针和引用失效。
  2. 对于deque,插入到除首尾位置之外的任何位置都会导致迭代器、指针和引用失效。如果在首尾位置添加元素,迭代器会失效,但指向存在的元素的引用和指针不会失效。
  3. 对于list和forward_list,指向容器的迭代器(包括尾后迭代器和首前迭代器)、指针和引用仍有效。
  4. 对于vector和string,指向被删元素之前元素的迭代器、引用和指针仍有效。
  5. 对于deque,如果在首尾之外的任何位置删除元素,那么指向被删除元素外其他元素的迭代器、引用或者指针也会失效。如果是删除deque的尾元素,则尾后迭代器也会失效,但是其他迭代器、指针和引用不受影响;如果是删除首元素,这些不会受到影响。
  6. 注意:当我们删除元素时,尾后迭代器总是会失效;
  7. 由于向迭代器添加原色和从迭代器删除元素的代码可能会使迭代器失效,因此必须保证每次改变容器的操作之后都正确的重新定位迭代器。这个建议对vector、string和deque尤为重要。

    vector操作:
    //除array外,所有标准课容器都提供灵活的内存管理,运行时可以动态添加删除元素来改变容量大小;//array容量固定;vector <int> vec;vec.push_front(t); //在vec头部创建一个值t,返回void;vec.insert(p,t);在迭代器p指向的元素之前创建一个t,返回指向新添加的元素的迭代器;vec.insert(p,n,t);//返回指向新添加的第一个元素的迭代器;//向一个vector、string或deque插入元素会使所有指向容器的迭代器、引用和指针失效。


  8. vector删除操作:(注意位置的移动)

    vector <int> vec;vec.erase(pos);//删除pos位置的数据,传回下一个数据的位置;若pos指向尾元素,则返回尾后迭代器,若pos是尾后迭代器,则函数行为未定义,返回voidvec.erase(begin(),end());//删除[begin,end)区间的数据,传回下一个数据的位置;若end()就是尾后迭代器,则返回尾后迭代器;vector vi={0,1,2,3,4,5,6,7,8};auto iter=vi.begin();while(iter!=vi.end())1{if(*iter%2){iter=vi.insert(iter,*iter);iter+=2;}else{iter=vi.erase(iter);//此处不需移动迭代器,iter指向删除元素之后的下一个数据;}}//此程序删除vector中的偶数值元素,并复制每个奇数值元素,调用insert和erase后都更新迭代器,因为两者都会使迭代器失效。插入操作:
    list <string>lst;auto iter=lst.begin();while(cin>>word)iter=lst.insert(iter,word);//等价于调用push_front;

    vector <int> vec;auto begin=vec.begin(), end=vec.end();//此处保存了end元素,添加元素后,失效。这个迭代器不再指向任何一个元素。while(begin!=end){++begin;begin=vec.insert(begin,11);++begin;}//所以,如果在一个循环中插入/删除deque、string、或vector中的元素,不能缓存end返回的迭代器;必须在每次插入操作后重新调用end(),不能在循环开始前保存它返回的迭代器;
    //更安全的方法:在每个循环步骤添加或者删除元素后重新计算end;while(begin!=vec.end()){++begin;begin=vec.insert(begin,11);++begin;}

    vector <int> vec{1,2,3,4,5};for(auto &i:vec){i*=i;//对于vec中的每个元素执行求元素平方;i是一个引用;}

    定义一个array,除了制定元素类型,还要制定容器大小;array类型不能赋值;赋值相关运算会导致指向左边容器内部的迭代器、引用和指针失效。而swap操作将容器内容交换不会导致指向容器的迭代器、引用和指针失效(容器类型为array和string的情况除外);除了array外,交换两个容器内容的操作保证会很快,元素本身并未交换,swap只是交换了两个容器的内部数据结构。
  9. 程序分析:调用erase后,不必递增迭代器,因为erase返回的迭代器已经指向序列中下一个元素。调用erase后,需要递增迭代器两次,insert在给定位置之前插入新元素,然后返回指向新插入元素的迭代器。因此,在调用insert后,iter指向新插入元素,位于我们正在处理的元素之前。将迭代器递增两次,恰好越过了新添加的元素和正在处理的元素,指向下一个未处理的元素。不要保存end返回的迭代器当我们添加/删除vector或string元素后,或在deque中沙鸥元素之外任何位置添加/删除元素后,原来end返回的迭代器总会失效。因此,添加或者删除元素的循环程序必须反复调用end,而不能在循环之前保存end返回的迭代器,一直当容器末尾使用。程序:处理容器中的每个元素,在其后添加一个新元素,循环跳过新添加的元素,只处理原有元素。每次循环后,定位迭代器,使其指向下一个原有元素。

  10. swap将容器内容交换不会导致指向容器的迭代器、引用和指针失效;(容器为array和string的情况除外);
  11. assign操作:
    vector <string> v1;vector <const char *>v2;v1=v2;//错误,类型不匹配;v1.assign(v2.cbegin(),v2.cend());//正确,对assign的调用将v1中的元素替换为迭代器指定范围中的元素的拷贝;assign第二个版本接受一个整型值和一个元素值,用指定书目且相同给定值的元素替换容器中原有的元素;list <string> s(1);//一个元素,为空string;s.assign(10,"hiya");//10个元素,每个都是hiya;
  12. emplace操作:emplace_front、emplace和emplace_back。这些操作构造而不是拷贝元素。调用emplace_back时,会在容器管理的内存空间中直接创建对象,而调用push_back则会创建一个局部临时对象,并将其压入容器中;

0 0