文章目录
介绍
C++Primer第五版第九章学习心得
顺序容器即指控制元素存储和访问与元素加入容器时的位置相对应
一、顺序容器概述
容器类型 | 介绍 |
---|---|
vector | 可变大小数组,尾部插入删除O(1) |
deque | 双端队列,头部、尾部插入删除O(1) |
list | 双向链表,不支持随机访问,任何位置插入删除快 |
forward_list | 单项链表,同上 |
array | 固定大小数组,快速随机访问,不能增加或删除元素 |
string | 类似vector,专门用于string |
容器操作比同类数据结构往往更优秀,更快
更具需要选择使用的容器类型
二、容器库概览
容器提供了一种层次操作,即某些操作是同类容器都适用的
每个容器都定义在一个头文件中,文件名与容器名相同
类型别名 | 介绍 |
---|---|
iterator | 该容器迭代器类型 |
const_iterator | 可以读取元素不能修改元素的迭代器 |
size_type | 无符号整数 |
difference_type | 有符号整数 |
value_tyoe | 元素类型 |
reference | 左值类型 与value_type &相同 |
const_reference | const版左值类型 |
构造函数 | 解释 |
---|---|
C c | 默认构造函数,构造空容器 |
C c1(c2) | 构造c2的拷贝c1 |
C c(b,e) | 构造c,将迭代器b,e间的元素拷贝 |
C c{a,b,d,e,f…} | 列表初始化c |
赋值与swap | 解释 |
---|---|
c1 = c2 | 将c1中的元素替代为c2 |
c1 = {a,b,c,d,…} | 列表赋值 |
a.swap(b) | 交换a,b元素 |
swap(a,b) | 同上 |
大小 | 解释 |
---|---|
c.size() | 元素中的数目,不支持forward_list |
c.max_size() | c可保存的最大数目 |
c.empty() | 是否为空 |
添加删除元素的操作不太相同,总体来说有
insert、emplace、clear、erase等
所有容器都支持==和!=,但不是所有都支持<、>
1.迭代器
c.begin() 第一个
c.end() 最后一个的下一个
c.cbegin()、c.cend() const类型
反向迭代器:
reverse_iterator 逆序迭代 ++为前一个元素
const_reverser_iterator
c.rbegin() c.rend()第一个的前一个和 最后一个元素
c.crbegin()、c.crend()
begin()、end()为一个左闭右开区间,目的是保证begin()可以安全地解引用
2.容器类型成员
需要通过作用域运算符来使用成员
3.容器定义和初始化
当类型和元素类型不匹配时,不能直接通过容器来转换,但是使用迭代器传递范围时,可以转换即可正确初始化。
list<string> authors = {"Milton","Shakespeare","Austen"};
vector<const char*> articles = {"a","an","the"};
list<string> a(authors);//正确匹配
deque<string> b(authors);//容器类型不匹配
vector<string> c(articles);//元素类型不匹配
forward_list<string> d(articles.begin(),articles.end());//可以将char *转换为string
3.array
array比较特殊,定义时除了指定元素类型还要制定容器大小。使用时也必须同时指定元素类型和大小
array<int,42> ia1;
array<int,10> ia2 = {0,1,2,3,4,5,6,7,8,9};//可以列表初始化
array<int,10> ia3 = {42};//第一个元素为42,剩余执行值初始化为0
array<int,10>::size_type i;
array<int>::size_type j;//错误,未指定大小
数组不能拷贝,但array可以,只是需要两个array的类型相同,即大小和元素类型相同
ia3 = {0,1,2,3,4,5,6,7,8,9};//错误,array无法通过列表化赋值,但可以定义
ia3 = ia2;//可以赋值,但类型必须相同
原因是左右的运算元素大小可能不同,因此无法通过列表赋值以及assign操作
4.赋值和swap
swap交换,有方法和成员函数都可以实现,不是通过拷贝实现,而是修改数据结构,所以速度为O(1),array也可swap,但是是拷贝速度较慢;
关于迭代器、指针引用是否失效:
除了string、array之外,其他均不会失效。
string会失效,因为指向的对象已经属于不同的容器了。
array不会失效,但是元素值已经交换了。
assign覆盖 成员函数有三种
均要求类型相同
seq.assign(iterator begin,iterator end);//迭代器间的元素
seq.assign(l);替换为初始化列表l中的元素
seq.assign(n,t);//替换为N个值为t的元素
三、顺序容器操作
1.添加
push_back(t)///尾部添加,单向链表不支持
emplace_back(t)//同上
push_front(t)//首部添加,vector和string不支持
emplace_front(t)//同上
insert(p,t)//迭代器p之间添加一个值为t的元素,返回新添加元素的迭代器
emplace(p,t)//同上
insert(p,n,t)//迭代器p之间添加n个值为t的元素,返回新添加第一个元素的迭代器
insert(p,b,e)//迭代器p之间迭代器b,e之间的元素,返回新添加第一个元素的迭代器,若未添加,返回p。要求b和e不能是p所属容器的迭代器
insert(p,l)//l为列表,其余同上
注意:插入元素会使迭代器失效,emplace执行的是构造,push等为拷贝,所以额外空间会小一些,但要求传入参数与构造函数匹配或一次隐式匹配。
2.访问
除了array都有front(),除了list都有back(),分别返回首尾元素的引用,但需要先判断是否为空
3.删除
pop_back()//确保非空,否则异常
pop_front()//同上
earse(iter)//删除迭代器所指元素,返回其后的元素迭代器,若是最后一个元素,返回end(),若是end(),则属于未定义错误
erase(iter1,iter2)
clear()//清空
4.forward_list操作
操作 | 说明 |
---|---|
lst.before_begin() | 相当于空值头节点的迭代器,方便删除或者在第一个元素前插入元素 |
lst.cbefore_begin() | const版本 |
lst.insert_after(p,t) | 在迭代器p之后插入元素t |
lst.insert_after(p,n,t) | 插入n个元素t |
lst.insert_after(p,b,e) | b,e是迭代器范围 |
lst.insert_after(p,li) | li是一个列表,返回插入的最后一个元素的迭代器 |
emplace_after(p,args) | 使用args在p后面创建一个元素,返回指向新元素的迭代器 |
lst.erase_after§ | 删除p后的元素,返回被删元素后的迭代器,可以是尾后迭代器。但P不能是尾元素或尾后元素 |
lst.erase(b,e) | 删除b,e迭代器之间元素 |
整体来看,基本都是针对迭代器之后的元素进行操作,不能在尾后迭代器插入和删除,不能删除尾元素后的元素 |
5.改变容器大小
除了array,都支持resize(),但会造成迭代器等失效
resize(n) 将容器大小修改为n,若少于n则默认初始化后面的元素,多余n则删除后面元素
resize(n,t) 同上,少于n则将新增元素值初始化为t
四、Vector对象如何增长
vector能够动态增加元素,当原有空间已满时,会分配新空间并将原元素移动到新空间中,为避免每次新增元素带来的时间开销,通常在分配新空间时,会分配更大的空间作为备用,而这个分配新空间的阈值就是capacity。
capacity()返回容器预分配空间大小
shrink_to_fit()将预分配空间大小修改为当前元素大小,是一种申请,可以忽略
reserve(n)将欲分配空间大小设置为n,当n小于当前capacity时不改变预分配空间
int main()
{
vector<int> a(5,1);
cout<<a.capacity()<<endl;//5
a.push_back(2);//size大于capacity,重新分配空间
cout<<a.capacity()<<endl;//10,默认为增加为2倍
a.shrink_to_fit();//预分配空间设置为当前元素数量
cout<<a.capacity()<<endl;//6
a.reserve(5);//预分配修改为5,但小于当前capacity,不改变大小
cout<<a.capacity()<<endl;//6
a.reserve(10);//大于capacity,修改为10
cout<<a.capacity()<<endl;//10
return 0;
}
reserve和resize的本质区别在于二者针对的对象不同,resize针对的是size,是实际存在的;reserve针对的是capacity,是预分配的空间而非实际值。
五、额外的string操作
string支持构造函数中的实参是string或者const char*,且支持使用部分值作为初始化对象
const char *cp = "Hello World!";
string s1(cp);
string s2(cp,6);//cp为数组,所以是拷贝6个字符"Hello "
string s3(s1,6);//s1为string,所以是从下标6的位置开始,"World!"
string s4(s1,6,5);//从下标6,拷贝5个元素,"World"
string s5(s1,6,20);//只拷贝到s1末尾
1.substr
substr(pos);从pos到末尾
substr(start,n)从start起n个元素,n大于剩余元素只拷贝到末尾
2.insert和erase
除了原有的insert和erase,还有特殊的insert(pos,n,ch),在pos位插入n个ch字符
erase(pos,n)从pos位起删除n个字符
3.append和replace
append是在末尾插入
replace是调用erase和insert的简写
4.搜索
找不到的返回值为string::npos,是一个const string::size_type类型,最好不要将其保存为int和其他有符号类型
方法 | 说明 |
---|---|
s.find(args) | 查找args第一次出现的位置 |
s.rfind(args) | 查找args最后一次出现的位置 |
s.find_first_of(args) | 查找args中任何一个字符第一次出现的位置 |
s.find_last_of(args) | 查找args中任何一个字符最后一次出现的位置 |
s.find_first_not_of(args) | 查找第一个不在args的字符 |
s.find_last_not_of(args) | 查找最后一个不在args的字符 |
args有四种形式:
c,pos 从pos开始查找字符c,pos默认为0
s2,pos 从pos开始查找字符串s2,pos默认为0
cp,pos cp为c风格字符串,pos默认为0
cp,pos,n 同上,查找cp的前n个字符, pos和n无默认值
5.compare
类似strcmp,有多种参数
6.数值转换
string str = to_string(n)可以将int n转换为string
stoi(s) 可以将s转换为浮点数,前提是s中第一个非空白字符必须是符号+、-或者是数字
六、容器适配器
适配器是一种机制,使得某事物的行为看起来像另一种事物一样,不适用array和forwar_list,因为该容器无法添加和删除任意元素
stack、queue、priority_queue都有其特殊操作
stack s;
s.pop();
s.push(item);
s.emplace(args);
s.top();
queqe Q;
Q.pop();
Q.push(item);
Q.front();
Q.back();
Q.top();
Q.emplace(args);
总结
关于容器的使用十分常见,还是需要多多练习才能熟练使用