C++ Primer 第9章 顺序容器 第一次学习笔记

来源:互联网 发布:网络开发项目管理课程 编辑:程序博客网 时间:2024/05/21 10:03

1. 顺序容器概述

#include <vector>//可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢#include <deque>//双端队列。支持快速随机访问。在头尾位置插入、删除速度都很快#include <list>//双向链表。只支持双向顺序访问。在list中任何位置进行插入、删除操作速度都很快#include <array>//固定大小数组。 支持快速随机访问。不能添加或删除元素 #include <string>//与vector相似的容器,但专门用于保存字符。随机访问快。在尾部插入、删除速度快/*通常,使用vector是最好的选择,除非有很好的理由选择其他容器可以定义一个容器,其元素的类型是另一个容器:vector<vector<string>> lines;此处lines是一个vector,其元素类型是string的vector



2. 左闭合范围

一个迭代器范围由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或尾元素之后的位置。

左闭合区间:[begin,end)表示范围自begin开始,于end之前结束(end不能指向begin之前的位置)

使用左闭合范围有三种方便的性质:

a) 如果begin==end,范围为空    

b) 如果begin!=end,则范围至少包含一个元素

c) 可以对begin递增若干次,使begin==end

因此,我们可以用循环处理一个元素范围,给定合法范围的begin和end,若范围为空,则退出循环



3. vector和string迭代器支持>、>=、<、<=关系运算符,但list不支持

list<int> lst1;list<int>::iterator iter1=lst1.begin(),iter2=lst1.end();/*while(iter1<iter2)这是不可以的诶*/while(iter1!=iter2) /*这才是对的*/ 



4. begin(返回指向第一个元素或第一个字符的迭代器)和end(返回指向容器或string对象尾元素的下一位置的迭代器)有多个坂(版)本:

a) rbegin和rend返回反向迭代器

b) 以c开头的坂本则返回const迭代器

list<string> a={"Milton","Shakespear","Austen"};auto it1=a.begin();//list<string>::iteratorauto it2=a.rbegin();//list<string>::reverse_iteratorauto it3=a.cbegin();//list<string>::const_iteratorauto it4=a.crbegin();//list<string>::const_reverse_iterator



5. 迭代器类型

a) 当不需要写访问时,应使用cbegin和cend

b) 如果对象是个常量,只能使用const_iterator

        vector<int> v1;const vector<int> v2;auto it1=v1.begin(),it2=v2.begin();        //error,it1和it2类型不一致auto it3=v1.cbegin(),it4=v2.cbegin();


6. 容器定义和初始化

#include <iostream>#include <vector>using namespace std;int main(){vector<int> v1;//默认初始化。如果不是array, 容器为空vector<int> v2(v1);//v2初始化为v1的拷贝。这两个容器必须是相同的容器类型,vector<int> v3=v1;//且保存的是相同的元素类型 vector<int> v4{1,2,3};//列表初始化,初始化为初始化列表中元素的拷贝, vector<int> v5={1,2,3};//列表中元素的类型必须与容器的元素类型相同 vector<int> v6(v1.begin(),v1.end());//初始化为两个迭代器指定范围中的元素的拷贝vector<int> v7(10,-1);//10个int元素,每个都初始化为-1vector<int> v8(10);//10个int元素,每个都初始化为0 return 0;} 
注意:当传递迭代器参数来拷贝一个范围时,不要求容器类型是相同的,只要能将要拷贝的元素转换为要初始化的容器的元素类型即可:
        vector<float> ivec1={0.1,0.5,5.6};vector<int> ivec2(ivec1.begin(),ivec1.end());



7. swap:交换两个相同类型容器的内容。

元素本身并未交换,swap只是交换了两个容器的内部数据结构,which means除string外,指向容器的迭代器、引用和指针在swap操作之后都不会失效,它们仍指向

swap操作之前所指向的那些元素。



8. 容器大小操作

每个容器都有三个与大小相关的操作:

a) size返回容器中元素的数目

b) empty当size为0时返回布尔值true

c) max_size返回一个大于或等于该类型容器所能容纳的最大元素数的值

d) 每个容器类型都支持相等运算符(==和!=);除了无序关联容器外的所有容器都支持关系运算符(>、>=、<、<=)

e) 关系运算符左右两边的运算对象必须是相同类型的容器,且必须保存相同类型的元素



9. 向顺序容器添加元素(vector和string不支持push_front和emplace_front)

c.push_back(t)//在c的尾部创建一个值为t或由args创建的元素c.emplace_back(args)//返回voidc.insert(p,t)//在迭代器p指向的元素之前创建一个值为r或由args创建的元素c.emplace(p,args)//返回voidc.insert(p,n,t)//在迭代器p指向的元素之前插入n个值为t的元素。//返回指向新添加的第一个元素的迭代器;若n为0,则返回p c.insert(p,b,e)//将迭代器p和e指定的范围内的元素插入到迭代器p指向的元素之前。b和e不能指向c中的元素。//返回指向新添加的第一个元素的迭代器;若范围为空,则返回p c.insert(p,il)//il是一个花括号包围的元素值列表。将这些给定值插入到迭代器p指向的元素之前。//返回指向新添加的第一个元素的迭代器;若列表为空,则返回p
a) 向一个vector、string或deque插入元素会使所有指向容器的迭代器、引用和指针失效

b) 当用一个对象初始化容器时(或将一个对象插入到容器中时),实际上放到容器中的是对象值的一个拷贝而不是其本身;随后对容器中元素的任何改变都不会影响到原始对象

c) 每个insert函数都接受一个迭代器作为其第一个参数,将元素插入到迭代器所指定的位置之前

        vector<int> vec{1,10,24,3};vec.insert(vec.begin(),4);for(auto c:vec)cout<<c<<" ";输出结果为4 1 10 24 3

d) insert还有一个版本接受的参数为一个元素数目和一个值,它将指定数量的元素添加到指定位置之前,这些值都按给定值初始化

        vector<string> svec{"hhh"};svec.insert(svec.end(),10,"A233");输出结果为hhh A233 A233 A233

e) 接受一对迭代器或一个初始化列表的insert坂本将给定范围中的元素插入到指定位置之前

        vector<string> svec1={"i","love","study","and"};vector<string> svec2={"study","makes","me","happy"};svec2.insert(svec2.begin(),svec1.begin(),svec1.end()-2);//将svec1最后的两个元素添加到svec2的开始位置for(auto c:svec2)cout<<c<<" ";svec1.insert(svec1.end(),{"i","want","to","be","a dalao"});for(auto n:svec1)cout<<n<<" ";

注意:拷贝的范围是左闭合区间,即第三行的svec1.end()-2,the string "study",没有被拷贝过去

f) 使用insert的返回值可以在容器中一个特定位置反复插入元素,因为insert函数返回的是新添加的(第一个元素的)迭代器

vector<string> svec={"anita","Anita","ANITA","gcn","GCN"};auto iter=svec.begin();string word;while(cin>>word)iter=svec.insert(iter,word);//等价于调用push_frontfor(auto c:svec)cout<<c<<" ";


10. 访问元素

a) 每个顺序容器都有一个front成员函数,除forward_list之外的所有顺序容器都有一个back成员函数。这两个操作分别返回首元素和尾元素的引用

c.back()//返回c中尾元素的引用,若c为空,函数行为未定义c.front()//返回c中首元素的引用,若c为空,函数行为未定义c[n]//返回c中下标为n的元素的引用,n是一个无符号整数,若n>=c.size(),函数行为未定义 c.at[n]//返回下标为n的元素的引用。如果下标越界,则抛出一out_of_range异常 

b) 不可以对空容器调用front和back

c) 在容器中访问元素的成员函数(front、back、下标和at)返回的是引用。如果容器是一个const对象,返回值是const引用。如果容器不是const,可以用返回的普通引用改变元素的值

vector<int> ivec={7,2,4,6,2,0,9};if(!ivec.empty()){ivec.front()=42;//将42赋予c中的第一个元素 auto &v1=ivec.back();//获得指向最后一个元素的引用cv1=1024;auto v2=ivec.back();//v2不是一个引用,只是ivec.back()的拷贝v2=0;//v2不是引用,未改变c中的元素 }for(auto a:ivec)cout<<a<<" ";//输出结果是42 2 4 6 2 0 1024


11. 删除元素(与vector和string不支持push_front一样,vector和string也不支持pop_front)

c.pop_back()//删除c中尾元素,返回void c.pop_front()//删除c中首元素,返回void c.erase(p)//删除迭代器p所指定的元素,返回一个指向被删元素之后元素的迭代器。若p指向尾元素,返回尾后迭代器。 c.erase(b,e)//删除迭代器b和e所指定范围内的元素。返回一个指向最后一个被删的元素之后元素的迭代器 c.clear()//删除c中所有元素,返回void 
a) pop_front和pop_back都返回void。如果需要弹出的元素的值,应在执行弹出操作之前保存它

b) 成员函数erase从容器中指定位置删除元素,两种形式的erase都返回指向删除的(最后一个)元素之后位置的迭代器。下面的栗子删除ivec中的所有奇数,调用了接受一个迭代器的erase坂本的函数

vector<int> ivec;int n;while(cin>>n)ivec.push_back(n);auto it=ivec.begin();while(it!=ivec.end()){//不为空if(*it%2)it=ivec.erase(it);//erase返回被删元素之后元素的迭代器else++it;}for(auto c:ivec)cout<<c<<" ";

c) 接受一对迭代器的erase坂本允许我们删除一个范围内的元素。依旧是左闭合区间,栗子在下面

        vector<int>ivec{0,1,2,3,4,5,6,7,8,9};        auto iter1=ivec.begin()+1,iter2=ivec.end()-2;               //*(ivec.end()-2)是8,前面的那个是1        ivec.erase(iter1,iter2);        for(auto c:ivec)        cout<<c<<"";

d) 为了删除一个容器中的所有元素,既可以调用clear函数,也可以用begin和end获得的迭代器作为参数调用erase

ivec1.clear();        ivec2.erase(ivec2.begin(),ivec2.end());        //等价调用,都是删除容器中所有元素



12. 改变容器大小

a) 用resize函数增大或缩小容器

c.resize(n)   //调整c的大小为n个元素。                      //若n<c.size(),则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化c.resize(n,t) //调整c的大小为n个元素。任何新添加的元素都初始化为值t 

b) 如果当前大小大于所要求的大小,容器后部的元素会被删除;如果当前大小小于新大小,会将新元素添加到容器后部

 vector<int> ivec(10,42);              //10个int,每个int的值都是42        ivec.resize(15);                      //将5个值为0的元素添加到ivec的末尾        ivec.resize(25,-1);                   //将10个值为-1的元素添加到ivec的末尾        ivec.resize(5);                        //从ivec的末尾删除20个元素



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

a) 在向容器vector或string添加元素后,且存储空间被重新分配,指向容器的迭代器、指针或引用都会失效;如果存储空间未重新分配,指向插入位置之前的元素的迭代器、指针和引用仍有效,但指向插入位置之后元素的迭代器、指针和引用都会失效

b) 删除vector或string的一个元素后,指向被删元素之前元素的迭代器、指针和引用仍有效

c) 删除元素时,尾后迭代器总会失效



14. 构造string的其他方法

string s1        string s2(s1)        string s2=s1                   //等价于s2(s1)        string s3("value")        string s3="value"        string s4(n,'c')

string有与其他顺序容器相同的构造函数,在前面。下面是新内容

string s(cp,n)                 //s是cp指向的数组中前n个字符的拷贝。此数组至少应该包含n个字符        string s(s2,pos2)              //s是strings2从下标pos2开始的字符的拷贝        string s(s2,pos2,len2)        //s是string s2从下标pos2开始len2个字符的拷贝。不管len2的值是多少,构造函数至多拷贝s2.size()-pose2个字符




15. substr操作
string s(cp,n)                 //s是cp指向的数组中前n个字符的拷贝。此数组至少应该包含n个字符        string s(s2,pos2)              //s是strings2从下标pos2开始的字符的拷贝        string s(s2,pos2,len2) //s是string s2从下标pos2开始len2个字符的拷贝。不管len2的值是多少,构造函数至多拷贝s2.size()-pose2个字符        string s("hello world");        string s2=s.substr(0,5);       //s2=hello        string s3=s.substr(6); //s3=world        string s4=s.substr(6,11);      //s4=world
substr操作返回一个string,它是原始string的一部分或全部的拷贝。可以传递给substr一个可选的开始位置和计数值



16. string搜索操作

a) string类提供了6个不同的搜索函数。每个搜索操作都返回一个string::size_type的值,表示匹配发生位置的下标;如果搜索失败,返回npos(初始化为值-1)

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中的字符

args必须是以下形式之一:

c,pos 从s中位置pos开始查找字符c。pos默认为0s2,pos 从s中位置pos开始查找字符串s2。pos默认为0 cp,pos 从s中位置pos开始查找指针cp指向的以空字符结尾的C风格字符串。pos默认为0 cp,pos,n 从s中位置pos开始查找指针cp指向的数组的前n个字符。pos和n无默认值



17.compare函数

a) 类似于strcmp,根据string是等于、大于还是小于参数指定的字符串,s.compare()返回0、正数或负数

b) 根据是要比较两个string还是一个string与一个字符数组,参数各有不同。以下是s.compare()的6种参数形式

s2                     比较s和s2pos1,n1,s2             将s中从pos1开始的n1个字符与s2进行比较pos1,n1,s2,pos2,n2     将s中从pos1开始的n1个字符与s2中从pos2开始的n2个字符进行比较cp                     比较s与cp指向的以空字符结尾的字符数组pos1,n1,cp             将s中从pos1开始的n1个字符与cp指向的以空字符结尾的字符数组进行比较pos1,n1,cp,n2       将s中从pos1开始的n1个字符与指针cp指向的地址开始的n2个字符进行比较




18.容器适配器

a) 标准库定义了三个顺序容器适配器:stack、queue和priority_queue

b) 一种适配器本质上是一种机制,能使某种事物的行为看起来像另外一种事物一样。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型。例如,stack适配器接受一个顺序容器,并使其操作起来像一个stack一样

c) 所有容器适配器都支持的操作和类型:

size_type        一种类型,足以保存当前类型的最大对象的大小        value_type       元素类型        container_type   实现适配器的底层容器类型        A a;             创建一个名为a的空适配器        A a(c);          创建一个名为a的适配器,带有容器c的一个拷贝        关系运算符       每个适配器都支持所有关系运算符:==、!=、<、<=、>和>=,                         这些运算符返回底层容器的比较结果        a.empty()        若a包含任何元素,返回false,否则返回true        a.size()         返回a中的元素数目        swap(a,b)        交换a和b的内容,a和b必须有相同类型,a.swap(b)     包括底层容器类型也必须相同

    



19.定义一个适配器

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

b) 可以使用除array和forward_list之外的任何容器类型来构造stack

c) queue可以构造于list或deque之上,但不能基于vector构造

d) priority_queue可以构造于vector或string之上




20.栈适配器(可以在vector、deque或list之上时间)

a) stack类型定义在stack头文件中。以下是stack所支持的操作:

s.pop()             删除栈顶元素,但不返回该元素值s.push(item)           创建一个新元素压入栈顶,该元素通过拷贝或移动item而来,s.emplace(args)        或者由args构造s.top()           返回栈顶元素,但不将元素弹出栈

下面是一个小栗子:

stack<int>intStack;   //空栈for(size_t ix=0;ix!=10;ix++)intStack.push(ix);             //intStack保存0到9十个数while(!intStack.empty()){      //intStack中有值就继续循环int value=intStack.top();intStack.pop();                //弹出栈顶值的元素,继续循环}

b) 每个容器适配器都基于底层容器类型的操作定义了自己的特殊操作。我们只可以使用适配器操作,不能使用底层容器类型的操作。例如,不能在一个stack上调用push_back,而必须使用stack自己的操作——push



21. 队列适配器(queue默认基于deque实现,priority_queue默认基于vector实现)

a) queue和priority_queue适配器定义在queue头文件中。以下是queue和priority_queue支持的操作

q.pop()            返回queue的首元素或priority_queue的最高优先级的元素,但不返回此元素q.front()          返回首元素或尾元素,但不删除瓷元素q.back()           只适用于queueq.top()            返回最高优先级元素,但不删除该元素,只适用于priority_queueq.push(item)       在queue末尾或priority_queue中恰当的位置创建一个元素,q.emplace(args)    其值为item,或者由args构造

b) 标准库queue使用一种先进先出的存储和访问策略:进入队列的对象被放置到队尾,而离开队列的对象则从队首删除

c) priority_queue允许为队列中的元素建立优先级,新加入的元素会排在所有优先级比它低的已有元素之前

 
原创粉丝点击