S09顺序容器

来源:互联网 发布:暴走漫画淘宝 编辑:程序博客网 时间:2024/06/03 17:07

S09顺序容器


一、顺序容器概述

1、顺序容器类型

vector        //可变大小数组,支持快速随机访问,在尾部之外的位置插入/删除元素很慢deque         //双端队列,支持快速随机访问,在头/尾位置插入/删除元素很快list          //双向链表,只支持双向顺序访问,在任何位置插入/删除元素很快forward_list  //单向链表,只支持单向顺序访问,在任何位置插入/删除元素很快array         //固定大小数组,支持快速随机访问,不能添加/删除元素string        //与vector相似但专门用于保存字符,支持快速随机访问,在尾部插入/删除很快

注意:尽可能使用新标准库的容器

二、容器库概览

1、一般每个容器都定义在一个头文件中,文件名与类型名相同,如list在list头文件中

2、容器库及支持的操作

类型别名    iterator            //此容器类型的迭代器类型const_iteratorsize_type           //无符号整型,足够保存此种容器类型最大可能容器的大小difference_type     //整型,足够保存两个迭代器之间的距离value_type          //元素类型reference           //相当于value_type&const_reference     赋值与交换   c1 = c2             //将c1中的元素替换为c2中的元素c1 = {a, b, ...}    //将c1中的元素替换为列表中的元素a.swap(b)           //交换a与b的元素swap(a,b)           //相当于a.swap(b)或b.swap(a)大小  c.size()            //c中元素的数量(不支持forward_list)c.max_size()        //c中可保存的最大数目c.empty()           //c中若有元素则范围true否则返回false添加与删除(不同容器中函数接口可能不同)c.insert(args)      //将args中的元素拷贝进cc.emplace(inits)    //使用inits构造c中的一个元素c.erase(args)       //删除args指定的元素c.clear()           //删除c中所有元素,返回void关系元算符   ==/!=               //所有容器都支持</<=/>/>=           //无序关联容器不支持获取迭代器   c.begin()/c.end()   //获取头元素和尾后元素的指针c.cbegin()/c.cend() //const_iterator反向(不支持forward_list)reverse_iterator    //按逆序寻址元素的迭代器const_reverse_iterator  c.rbegin()/c.rend() //获取尾元素和头前元素的指针c.crbegin()/c.crend()---------------------------------------------------------------| before_first_elem | elem_1 | ... | elem_n | after_last_elem |---------------------------------------------------------------          |              |              |            |       c.rend()      c.begin()      c.rbegin()    c.end()

注意:反向容器的迭代器递增对应着反向,递减对应着正向

vector<int> a{ 0,1,2,3,4,5 };for (auto st = a.rbegin(), ed = a.rend(); st != ed; ++st){...}  //++则从5->0,而--从5->?会出错

3、迭代器:标准容器类型上的所有迭代器都通过解引用来访问元素,且当满足以下两个条件时构成范围的迭代器
(1)指向同一个容器中的元素或尾后元素
(2)end不在begin之前,即反复递增begin一定可以达到end

int ia[] = { 0,1,1,2,3,5,8,13,21,55,89 };vector<int> ivec.assign(ia,ia+11);            //可以实现数组拷贝到向量中

注意:指针是迭代器的一种,迭代器是指针抽象提升后的概念

4、容器定义和初始化
(1)新容器创建为另一个容器的拷贝时,可以直接拷贝整个容器或拷贝由迭代器指定的范围内元素

  • 直接拷贝:要求两个容器的类型及其元素类型必须严格匹配
  • 范围迭代器拷贝:不要求容器类型相同,元素类型只要转换后能匹配也可以
list<string> authors = {...};vector<const char*> articles = {...};list<string> list2(authors);      //正确,完全匹配deque<string> authlist(authors);  //错误,容器类型不匹配vector<string> words(aricles);    //错误,元素类型不匹配forward_list<string> words(articles.begin(), articles.end());   //正确,const char*会转换为string

(2)如果元素类型是内置类型或者有默认构造函数,则可以只提供容器大小的参数,若没有默认构造函数则必须显式提供初始值,另外只有顺序容器接受大小参数,关联容器没有
(3)array:具有固定大小,定义时除了提供元素类型还要提供固定大小值,在使用其别名时也要提供大小,大小是类型的一部分

array<int, 10>                 //类型为:保存10个int的数组array<int, 10>::size_type I;   //不能省略10而写成array<int>::size_type会出错

5、容器赋值与交换

assign操作(不适用于array和关联容器,仅顺序容器,且允许类不同但元素相容的赋值)seq.assign(b, e)    //将seq的元素换为迭代器b/e范围内的元素,b/e不能指向seq自身seq.assign(il)      //seq的元素替换为初始化列表il的元素seq.assign(n,t)     //seq的元素替换为n个值为t的元素

注意:赋值一般(除了swap)会导致指向左侧容器的迭代器、指针、引用失效

注意:swap一般(除了array和string类型的容器)不会导致失效,因为元素本身并没有被移动,假定一个迭代器指向vec1[3],vec1与vec2交换后该迭代器实际指向vec2[3]

6、容器关系运算符:只有容器类型一样,元素类型一样时才可以用关系运算符,且比较过程类似于string的比较,逐个元素调用关系运算符

注意:容器的关系运算符基于元素的关系运算符,只有元素定义了关系运算,且非无序关联容器才可以比较

三、顺序容器操作

1、顺序容器主要操作参考《C++primer 5th》p.305

2、向容器添加/删除元素可能会导致所有指向容器的迭代器、引用和指针失效

注意:由于向迭代器添加/删除元素可能会导致失效,因此必须保证每次改变容器的操作之后都正确的重新定位迭代器

3、容器初始化或插入新元素时,都是使用了原始对象的副本放入容器,故容器元素是拷贝(后面会讲到std::move和右值引用)

4、使用emplace操作,push_front/insert/push_back将元素类型的对象传递给容器,并拷贝到容器中,而对应的emplace_front/emplace/emplace_back则是将参数传递给元素类型的构造函数,使用参数在容器管理的内存中直接构造元素,因此传递的参数必须符合元素类型的构造函数

vector<Sales_data> c;c.emplace_back("9-999", 25, 15.99);          //正确,将根据参数构造出一个新元素,基于Sales_data的构造函数c.emplace_back();                            //正确,使用了Sales_data的默认构造函数c.push_back("9-999", 25, 15.99);             //错误c.push_back(Sales_data("9-999", 25, 15.99)); //正确,临时生成了Sales_data对象并复制进容器

5、访问元素:不能在容器是空时访问元素,程序员应确保访问的元素存在

c.back()    //返回c中尾元素的引用,若是const的容器则返回const的引用,下同c.front()   //返回c中首元素的引用c[n]        //返回c中下标为n的元素的引用,注意下标不能越界,越界时发生运行时错误直接退出c.at(n)     //返回c中下标为n的元素的引用,注意下标不能越界,尽可能用at(),下标越界先抛出异常if(!c.empty()){    auto val1 = *c.begin(), val2 = c.front();  //由于auto变量用引用初始化时直接会用本体,因此没有&的含义    auto last = c.end();    auto val3 = *(--last), val4 = c.back();    //c不是forward_list则正确,val3和val4是尾元素的拷贝    auto &val5 = c.front();                    //val5才是首元素的引用}

6、删除元素:不能在元素不存在时删除,程序员应确保删除的元素存在

c.pop_back()    //删除c中尾元素,返回voidc.pop_front()   //删除c中首元素,返回voidc.erase(p)      //删除迭代器p所指的元素,返回被删元素之后元素的迭代器c.erase(b, e)   //删除迭代器b和e之间所有的元素[b,e),若b==e则什么也不做,返回e指向元素的迭代器,可以认为是返回ec.clear()       //删除所有元素,返回void

注意:由于数据结构本身的特性,不同的容器支持的操作有差别,forward_list诸多操作有特殊的实现,主要是before_begin/insert_after/emplace_after/erase_after

7、改变容器大小:当减小时多余元素会被删除,当扩大时不足元素会被补齐,只改变元素量size,不改变容量capacity,注意区分capacitysize

c.resize(n)      //调整c的大小为n个元素,新元素进行值初始化c.resize(n, t)   //调整c的大小为n个元素,新元素初始化为t

四、vector对象是如何增长的

1、管理容量

c.shrink_to_fit()//将capacity()减少为与size()相同大小,适用vector/string/deque(只是请求,不保证退还内存)c.capacity()     //不重新分配内存空间的话,c可以储存的元素数量,适用vector/stringc.reserve(n)     //分配至少能容纳n个元素的内存空间,适用vector/string

注意:当n大于当前capacity时,reserve至少分配与n一样大的内存空间;当n小于等于当前capacity时,reserve什么也不做,因此reserve不会减少容器占用内存,类似的resize也同样如此

五、额外的string操作

1、构造string的其他方法

string s(cp,n)           //s是cp指向的数组前n个字符的拷贝string s(s2, pos2)       //s是string s2从下标pos2开始的字符的拷贝string s(s2, pos2, len2) //s是string s2从下标pos2开始,长度为min(len2, s2.size()-pos2)的字符的拷贝s.substr(pos, n)         //返回一个string,包含s中从pos开始的n个字符的拷贝,pos默认为0,n默认为s.szie()-pos

2、操作string的其他方法,参考《C++ primer 5th》p.323,主要是

insert/erase/assign/append/replace

3、搜索string的其他方法,参考《C++ primer 5th》p.325,主要是

find/rfindfind_first_of/find_last_offind_first_not_of/find_last_not_of

4、compare函数及其各重载形式,参考《C++ primer 5th》p.327

5、数值转换

to_string(val)   //返回val的string表示stoi(s, p, b)    //s为需要转换的string,要求必须以数值中可能出现的字符为开头,转换为整数stol(s, p, b)    //返回值类型如to后的字母表示的类型,例如stoi返回int,stol返回longstoul(s, p, b)   //b表示基数,默认为10,p是size_t*指针,保存s中第一个非数值字符的下标,默认为0即不保存下标stoll(s, p, b)stoull(s, p, b)stof(s, p)stod(s, p)stold(s, p)

六、容器适配器

1、适配器:一种能使某种事物的行为看起来像另一种事物的机制,一个容器适配器接受一种已有的容器类型,使其行为看起来像另一种不同的类型

2、容器适配器的种类及默认接受的已有容器类型

stack           //默认用deque实现,定义在stack头文件中queue           //默认用deque实现,定义在queue头文件中priority_queue  //默认用vector实现,定义在queue头文件中

3、每个适配器都定义了两个构造函数
(1)默认构造函数:创建空对象,接受一个容器,拷贝该容器来初始化适配器

deque<int> deq;stack<int> stk(deq);    //从deq拷贝元素到stk初始化一个新的stack

(2)重载默认容器的构造函数:将一个命名的顺序容器作为第二个类型参数来重载默认容器类型

stack<string, vector<string>> str_stk;   //stack默认用deque实现,这里重载,用vector来实现stack

注意:对于一个给定的适配器,可以使用的容器是有限制的;且必须用适配器提供的操作,不能直接使用容器提供的操作,例如stk.push(x)可以,而不能stk.push_back(x)

4、栈适配器

stack<int> s;          //默认基于deque,也可以用list或vectors.pop()                //删除栈顶元素,但不返回该删除的元素s.push(item)           //创建一个新元素压入栈顶,可以从item而来或是由args构造s.emplace(args)        //根据args构造元素加入ss.top()                //返回栈顶元素,但不将该元素弹出栈

5、队列适配器

queue<int> q;          //默认基于deque,也可以用list或vectorpriority_queue<int> q; //默认基于vector,也可以用dequeq.pop()                //删除queue的首元素或priority_queue的最高优先级元素,但不返回此元素q.front()              //返回首元素,但不删除该元素,只适用queueq.back()               //返回尾元素,但不删除该元素,只适用queueq.top()                //返回最高优先级元素,但不删除该元素,只适用priority_queueq.push(item)           //创建一个新元素放入queue末尾或插入priority_queue合适的位置q.emplace(args)        //根据args构造元素加入q

注意:对于priority_queue,默认根据<进行优先级排序,如果需要自定义元素之间的比较,则要重载这个默认排序

priority_queue<T> priqueue;                                    //默认构造优先队列的方式,基于T类型的<操作priority_queue<T, vector<T>, decltype(comp)*> priqueue(comp);  //在comp函数中自定义排序规则的优先队列
原创粉丝点击