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

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>::iterator
auto it2=a.rbegin();	//list<string>::reverse_iterator
auto it3=a.cbegin();	//list<string>::const_iterator
auto 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元素,每个都初始化为-1
	vector<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)			//返回void

c.insert(p,t)				//在迭代器p指向的元素之前创建一个值为r或由args创建的元素
c.emplace(p,args)			//返回void

c.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_front
	for(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();			//获得指向最后一个元素的引用c
		v1=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默认为0
	s2,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和s2
	pos1,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()           只适用于queue
	q.top()            返回最高优先级元素,但不删除该元素,只适用于priority_queue
	q.push(item)       在queue末尾或priority_queue中恰当的位置创建一个元素,
	q.emplace(args)    其值为item,或者由args构造

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

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



























































  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值