顺序容器
顺序容器类型
vector
可变大小数组.支持快速随机访问.在尾部之外的位置插入or删除元素可能很慢.
deque
双端队列. 支持快速随机访问. 从头尾位置插入or删除元素很快.
list
双向链表. 只支持双向顺序访问. 在`list中任何位置进行插入or删除操作都比较快.
array
固定大小数组.支持快速随机访问. 不能添加or删除元素.
string
与 vector
类似. 随机访问快.在尾部插入or删除快.
容器操作
// 构造函数
C c; // 默认构造函数,构造空容器
C c1(c2); // 构造c2的拷贝c1
C c(b,e); // 构造c,将迭代器b和e指定范围内的元素拷贝到c (array不支持)
C c{a,b,c}; // 列表初始化{}
// 赋值与swap
c1=c2; // 将c1中的元素替换c2中的元素
c1={a,b,c...}; // 将c1中的元素替换为列表中的元素! (array不支持)
a.swap(b); // 交换a,b的元素
swap(a,b); // 与a.swap(b)等价
// 大小
c.size(); // c中元素的数目 (不支持 forward_list)
c.max_size(); // c可保存的最大元素数目
c.empty(); // 若c中存储元素,返回false.否则为true
// 添加/删除元素 (不适用于array)
// 注:在不同容器中,这些操作的接口都不同
c.insert(args); // 将args中的元素拷贝进c
c.emplace(inits); // 使用inits构造c中的一个元素
c.erase(args); // 删除args指定元素
c.claer(); // 删除c中所有元素,返回void
// 获取迭代器
c.begin();c.end(); // 返回指向c的首元素和尾元素之后位置的迭代器
c.cbegin();c.cend();// 返回const_iterator
// 关系运算符
== != // 所有容器都支持的相等/不等 运算符
<,<=,>,>= // 关系运算符 (无序关联容器不支持)
// 反向容器的额外成员(不支持forward_list)
reverse_iterator; // 按逆序寻址元素的迭代器
const_reverse_iterator; // 不能修改元素的逆序迭代器
c.rbegin();c.rend(); // 返回指向c尾元素和首元素之前位置的迭代器
c.cebegin();c.crend(); // 返回const_reverse_iterator
迭代器
迭代器范围由一对迭代器表示.
左闭右开 区间!
begin 与 end 相等,范围为空
不需要写访问时,应使用
cbegin
与cend
容器定义与初始化
C c; // 默认构造函数.若是一个array,则c中元素按默认方式初始化.否则为空.
C c1(c2); // c1初始为为c2的拷贝.注意! 类型相同(容器类型,元素类型).对于array,大小还必须相同.
C c1=c2;
C c{a,b,c}; // 初始化为初始化列表中元素的拷贝.
C c={a,b,c};
C c(b,e); // 迭代器初始化.c初始化为b和e指定范围中的元素的拷贝.
/* 只有顺序容器(不包括array)的构造器才能大小参数 */
C seq(n); // seq包含n个元素.这些元素进行了初始化.
C seq(n,t); // 初始化了n个元素.值都为t.
赋值与swap
- 赋值相关运算会导致指向 左边容器 的内部迭代器,引用和指针失效!
- swap操作将容器内部内容交换 不会 导致指向容器的迭代器,引用和指针失效.(容器类型为array和string的情况除外)
c1=c2; // c1中的元素替换为c2中的元素.
C={a,b,c}; // array不支持
swap(c1,c2);
c1.swap(c2);
- assign操作不适用于关联容器和array! 仅顺序容器!
seq.assign(b,e); // 迭代器b,e不能指向seq中的元素
// 由于旧元素被替换掉了,传递给assign的不能是调用的assign的容器.
seq.assign(il); // 替换为初始化列表 il 中的元素
seq.assign(n,t); // 替换为n个t值的元素
- 统一使用非成员版本的swap是一个好习惯.
swap(a,b);
向顺序容器添加元素
顺序容器与关联容器的不同之处在于 二者组织元素的方式.
这些不同之处直接关系到元素如何 存储,访问,添加,删除
-
非常重要的一点. 除了 forward_list 其他的顺序容器都是前插!!!
// 这些操作会改变容器的大小.array不支持!
c.push_bacck(t); // 在c的 尾部 创建一个值为t 或由args创建 的元素.
c.emplace_back(args);
c.push_front(t); // 在c的 头部 创建一个值为t 或由args创建 的元素.
c.emplace_front(args);
c.insert(p,t); // 在迭代器p指向的元素 之前 添加一个元素.
c.emplace(p,args);
c.insert(p,n,t); // 在p指向的元素 之前 添加 n个 值为t的元素
c.insert(p,b,e); // 将迭代器b和e指定范围的元素插入到 p指向的元素之前!
c.insert(p,il); // il是一个花括号包围的 元素值列表 ,将这些值插入到p指向元素之前.
forward_list
有自己专有版本的insert
和emplace
;forward_list
不支持push_back
和emplace_back
;vector
和string
不支持push_front
和emplace_front
;向一个
vector
,string
或deque
插入元素会使所有指向容器的迭代器,引用,指针失效.vector
不支持push_front
.但是 我们可以插入到begin()
之前!!!svec.insert(sevc.begin(),"hello!"); // 相当于push_front()
将元素插入到
vector
,deque
,string
中的任何位置是合法的, 但很耗时.使用
insert
的返回值,可以容器中的特定一个位置反复的插入元素:list<stirng> lst; auto iter = lst.begin(); while(cin>>word) iter = lst.insert(iter,word); //等价与 push_front
emplace
函数是在容器中 直接构造 元素.传递给emplace
函数的参数必须与 元素类型的构造函数 相匹配.
访问元素
c.back(); // 返回c中尾元素的引用;
c.front(); // 返回c中头元素的引用;
c[n]; // 返回下标为n的元素的引用.n是一个无符号整数
c.at(n); // 返回下标为n的元素的引用. 若越界!会抛出一个 out_of_range的异常
at
和下标操作
只适用于string vector deque array
back
不适用于forward_list
访问成员函数返回的是 引用
若容器是一个
const
对象,则返回的是const
的引用;否则,普通引用,可用来改变元素的值;
确保下标访问合法,可以使用
at
成员函数*iter
解引用迭代器也可以访问元素
删除元素
- 删除元素会改变容器大小,不适用于 array
// 删除元素会改变容器大小,不适用于 array
c.pop_back(); // 删除c中尾元素,返回void
c.pop_front(); // 删除c中首元素,返回void
c.erase(p); // 删除迭代器p指向的元素,返回一个指向被删除元素 之后 的迭代器!!!
c.erase(b,e); // 删除b和e所指定范围的元素,返回一个指向最后一个被删除元素 之后 的迭代器!!!
c.clear(); // 删除c中所有元素,返回void
forward_list
有特殊版本的erase
forward_list
不支持pop_back()
;vector
和string
不支持pop_front()
;删除
deque
中 除首尾之外 的任何元素都会使所有迭代器,引用和指针失效指向
vector
和string
中 删除点之后 的迭代器,引用,指针都会失效.删除元素的成员函数并不会检查其参数! 在删除元素之前,请确保它们存在
pop_front
和pop_back
成员函数返回void,若需要弹出的值! 必须在执行弹出操作前保存它!!!使用
front()
/back()
erase
函数返回的是 指向删除的 最后一个元素之后 位置的迭代器
特殊的 forwartd_list
操作
为什么
forward_list
需要特殊版本的添加和删除操作呢?对于单链表来说.添加或删除一个元素.它前面的元素内容也会发生变化.
为了删除或添加一个元素.必须访问到它的前驱元素.
在一个
forward_list
中添加or删除元素的操作都是通过改变 给定元素之后的元素来完成的首前 迭代器:
before_begin
forward_list
在添删元素之前,我们必须关注两个迭代器! 指向我们要处理的元素 and 指向其前驱
lst.before_begin(); // 返回指向链表首元素之前 不存在元素 的迭代器,不能解引用!
lst.cbefore_begin();
list.insert_after(p,t); // 在迭代器p之后 的位置插入元素,t是一个对象!!!
list.insert_after(p,n,t); // n 是数量
list.insert_after(p,b,e); // b,e表示范围的一对迭代器
list.insert_after(p,il); // il是一个花括号列表.
// 返回 一个指向最后一个插入的元素之后的迭代器
emplace_after(p,args); // 使用args在p指定位置之后 创建一个元素.返回一个指向这个新元素的迭代器!
lst.erase_after(p); // 删除p指向位置之后的元素!
lst.erase_after(b,e); // 删除从b之后(不包含b)到e之间的元素!
// 返回 一个指向被删元素之后元素的迭代器!
改变容器大小
// resize 来增大或减小容器.
c.resize(n); // 调整c的大小为n个元素.
c.resize(n,t); // 调整c的大小为n.任何 新添加的 元素值为t
resize
不支持array
- 若
resize
缩小容器,则指向被删除元素的迭代器,引用,指针都会失效! 对
vector
stirng
deque
进行resize
可能导致迭代器,指针,引用失效关于容器大小的操作.
成员函数size(); // 返回当前容器中元素的个数 成员函数empty(); // 当size为0时返回真,否则返回假 成员函数max_size(); // 返回一个大于或等于该类型容器所能容乃的最大元素的值
关系运算符
比较两个容器实际上是进行元素的逐对比较.
vector
对象是如何增长的
元素必须是连续存储的.如没有新空间容纳元素,容器必须 分配新的内存空间保存已有元素和新元素 将已有元素从旧位置移动到新空间中,然后添加新元素,释放旧空间!!
为了避免上一条代价,采用 减少容器空间重新分配次数的策略 !!! 通常会分配比新空间要求更大的内存空间!
管理容量的成员函数
// shrink_to_fit 只适用于 vector string deque // capacity 和 reserve 只适用于 vector string c.shrink_to_fit(); // 请将capacity() 减少为 size() 相同大小 c.capacity(); // 不重新分配内存空间的话,c可以容纳多少元素 c.reserve(n); // 分配至少容纳n个元素的内存空间
reserve
并不改变容器中元素的数量,仅影响vector
预先分配多大的内存空间只有当需要的内存空间超过当前容量时
reserve
调用才会改变vector
的容量capacity
是指不重新分配内存空间下,可以容纳多少元素;size
是指它已经保存的元素的数目;每个
vector
实现都可以选择自己内存分配策略,原则: 只有当迫不得已时才会分配新的内存空间
额外的 string
操作
除了顺序容器共同的操作外.string类型还提供了额外的一些操作.如
- string类与C风格字符数组之间的转换
- 增加下标替代迭代器的版本
- 大量函数
构造 string
的其他方法
// n len2 和 pos2 都是无符号值
string s(cp,n); // s是cp指向的数组中前n个字符的拷贝.此数组至少应该包含n个字符
string s(s2,pos2); // s是string s2 从下标pos2开始的字符的拷贝
string s(s2,pos2,len2); // s是string s2 从下标pos2开始的len2个字符的拷贝
- 通常,当从一个
const char*
创建string
时,指针指向的数组必须是 以空字符串结尾的 ,拷贝操作遇到 空字符 停止!!! 若传递给构造函数一个 计数值 ,数组 不必以空字符结尾.
若未传递 计数值 且 未以空字符结尾.构造函数的行为 未定义
const char *cp = "hello world!!!"; // 是以空字符为结尾 char noNULL[] = {'H','i'}; // 没有空字符结尾 string s1(cp); // s1=="hello world!!!" string s2(noNULL,2); // s2=="Hi" // 因为有len.
substr
操作!!!
substr
返回一个string
其实原始字符串或部分字符串的拷贝.
s.substr(pos,n); // 返回一个stirng,包含s从pos开始的n个字符的拷贝.pos默认为0,n默认为s.size();
// n是大小,长度!!!
改变 string
的其他操作
除了顺序容器的赋值运算符, assign
, insert
, erase
操作
- 接受下标版本的 insert和erase版
- C风格字符串数组的insert和assign
- append 和 replace 函数
s.insert(pos,args); // 在pos之前!!! 插入args指定的字符.pos可以是下标or迭代器.
// 接受下标的返回一个指向s的引用; 接受迭代器返回指向 第一个插入字符 的迭代器
s.erase(pos,len); // 删除从pos开始的len个字符.若len被省略,删除从pos开始到结尾,返回s的一个引用
s.assign(args); // 将s中的字符替代为args指定的字符.返回一个指向s的引用
s.append(args); // 将args追加到s.返回一个指向s的引用
s.replace(range,args); // 删除s中范围range内的字符,替换为args指定字符.
// range: 1.一个下标和一个长度 2. 一对指向s的迭代器
append assign insert replace
函数有多个重载版本- 并不是每个函数都支持所有形式的参数
string
搜索操作
- 搜索操作返回的是一个
string::size_type
值,表示 匹配发生位置的下标 - 搜索失败,返回的是
string::npos
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中查找s2字符串
cp,pos; // 从s中pos位置开始查找 指针cp指向的以空字符结尾的C风格字符串.pos默认为0
cp,pos,n; // 从s中pos位置开始查找cp指向的数组的前n个字符.pos 和 n 无默认值
compare
函数
- 标准库string提供一组
compare
函数,这些函数与C标准库的strcmp
函数相似 s.compare
返回0 正数 负数
// s.compare()的几种参数形式
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字符 比较
数值转换
// string和数值之间的转换
to_string(val);
stoi(s,p,b); // 返回s的起始子串(整数的内容)数值,b表示转换所用的基数.默认是10;p是size_t指针.
stol(s,p,b);
stoul(s,p,b);
stoll(s,p,b);
stoull(s,p,b);
stof(s,p); // f返回s的起始子串(表示浮点数内容)的数值.
stod(s,p);
stold(s,p);
- 若string不能转换为一个数值,这些函数抛出一个 invalid_argument 异常
- 若 转换得到的数值 无法用任何类型表示,抛出一个 out_of_range 异常
重点!!!
to_string(val); // 将数值转换成 string啊!!!
stoi(s); // 将string 转成 int
容器适配器
- 除了顺序容器外,标准库还定义了三个 顺序容器适配器
stack
queue
priority_queue
栈 / 队列 / 优先队列(堆)- 适配器 是一种机制!使得某种事物的行为看起来像另一个.
// 所有容器适配器 都支持的操作与类型
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 内容,ab类型相同,包括底层容器的类型
a.swap(b);
- 所有的适配器都要求容器具有添加 删除元素的能力
- 适配器不能构造在array之上,也不能用forward_list来构造适配器
栈适配器
// 栈操作
s.pop(); // 删除栈顶元素,但不返回该元素值!!!
s.push(item); // 创建一个新元素 压入栈顶
s.emplace(args);
s.top(); // 返回栈顶元素
- 只能使用适配器操作! 不能使用底层容器类型的操作
- 栈 默认是基本
deque
实现的,也可以在list
和vector
上实现
队列适配器
// queue 和 priority_queue 操作
q.pop(); // 返回queue的首元素 或 priority_queue的最高优先级元素,但不能删除此元素
q.front(); // 返回首元素 or 尾元素,但不能删除此元素
q.back(); // 只适用于 queue!
q.top(); // 返回最高优先级元素,但不能删除此元素,只适用于 priority_queue
q.push(item); // 在queue末尾 或 priority_queue中恰当的位置创建一个元素,其值为tiem,或由args构造
q.emplace(args);
queue
默认基于deque
实现. 也可以用list
vector
实现priority_queue
默认基于vector
实现. 也可以用deque
实现priority_queue
允许我们为队列中的元素建立优先级.新加入的元素会排列在所有优先级比它低的已有元素之前!!!