IO库与STL.
IO库
- istream:输入流类型,提供输入操作。
- ostream:输出流类型,提供输出操作
- cin:一个
istream
对象,从标准输入读取数据。 - cout:一个
ostream
对象,向标准输出写入数据。 - cerr:一个
ostream
对象,向标准错误写入消息。 >>
运算符:用来从一个istream
对象中读取输入数据。<<
运算符:用来向一个ostream
对象中写入输出数据。- getline函数:从一个给定的
istream
对象中读取一行数据,存入到一个给定的string
对象中。
IO类
- iostream头文件:从标准流中读写数据,
istream
,ostream
,iostream
等。 - fstream头文件:从文件中读写数据,
ifstream
,ofstream
,fstream
等。 - sstream头文件:从字符串中读写数据,
istringstream
,ostringstream
,stringstream
等。
IO对象无拷贝或赋值
- 1.IO对象不能存在容器里.
- 2.形参和返回类型也不能是流类型。
- 3.形参和返回类型一般是流的引用。
- 4.读写一个IO对象会改变其状态,因此传递和返回的引用不能是
const
的。
条件状态
状态 | 解释 |
---|---|
strm:iostate | 是一种机器无关的类型,提供了表达条件状态的完整功能 |
strm:badbit | 用来指出流已经崩溃 |
strm:failbit | 用来指出一个IO操作失败了 |
strm:eofbit | 用来指出流到达了文件结束 |
strm:goodbit | 用来指出流未处于错误状态,此值保证为零 |
s.eof() | 若流s的eofbit 置位,则返回true |
s.fail() | 若流s的failbit 置位,则返回true |
s.bad() | 若流s的badbit 置位,则返回true |
s.good() | 若流s处于有效状态,则返回true |
s.clear() | 将流s中所有条件状态位复位,将流的状态设置成有效,返回void |
s.clear(flags) | 将流s中指定的条件状态位复位,返回void |
s.setstate(flags) | 根据给定的标志位,将流s中对应的条件状态位置位,返回void |
s.rdstate() | 返回流s的当前条件状态,返回值类型为strm::iostate |
上表中,strm
是一种IO类型,(如istream
), s
是一个流对象。
管理输出缓冲
每个输出流都管理一个缓冲区,执行输出的代码,文本串可能立即打印出来,也可能被操作系统保存在缓冲区内,随后再打印。
刷新缓冲区,可以使用如下IO操纵符:
- endl:输出一个换行符并刷新缓冲区。
- flush:刷新流,单不添加任何字符。
- ends:在缓冲区插入空字符
null
,然后刷新。 - unitbuf:告诉流接下来每次操作之后都要进行一次
flush
操作。 - nounitbuf:回到正常的缓冲方式。
1 | cout << unitbuf; //所有输出操作后都会立即刷新缓冲区。 |
2 | //... 任何输出操作都立即刷新,无缓冲。 |
3 | cout << nounitbuf; //回到正常的缓冲方式。 |
文件输入输出
- ifstream从一个给定文件读取数据。
- ofstream向一个给定文件写入数据。
- fstream可以读写给定文件。
文件流:需要读写文件时,必须定义自己的文件流对象,并绑定在需要的文件上。
fstream特有的操作
操作 | 解释 |
---|---|
fstream fstrm; | 创建一个未绑定的文件流。 |
fstream fstrm(s); | 创建一个文件流,并打开名为s 的文件,s 可以是string 也可以是char 指针 |
fstream fstrm(s, mode); | 与前一个构造函数类似,但按指定mode 打开文件 |
fstrm.open(s) | 打开名为s 的文件,并和fstrm 绑定 |
fstrm.close() | 关闭和fstrm 绑定的文件 |
fstrm.is_open() | 返回一个bool 值,指出与fstrm 关联的文件是否成功打开且尚未关闭 |
当一个fstream对象被销毁时,close会自动被调用。
文件模式
文件模式 | 解释 |
---|---|
in | 以读的方式打开 |
out | 以写的方式打开 |
app | 每次写操作前均定位到文件末尾 |
ate | 打开文件后立即定位到文件末尾 |
trunc | 截断文件 |
binary | 以二进制方式进行IO操作。 |
默认情况下,打开一个ofstream时,文件内容会被丢弃。保留被ofstream打开的文件中已有数据的唯一方法是显示指定app
或in
模式ofstream app("file", ofstream::out | ofstream::app)
。
string流
头文件sstream
定义了三个类型来支持内存IO:
- istringstream从
string
读取数据。 - ostringstream向
string
写入数据。 - stringstream可以读写给定
string
。
stringstream特有的操作
操作 | 解释 |
---|---|
sstream strm | 定义一个未绑定的stringstream 对象 |
sstream strm(s) | 用s 初始化对象 |
strm.str() | 返回strm 所保存的string 的拷贝 |
strm.str(s) | 将s 拷贝到strm 中,返回void |
顺序容器
类型 | 说明 |
---|---|
vector | 可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。 |
deque | 双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。 |
list | 双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除速度都很快。 |
forward_list | 单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。 |
array | 固定大小数组。支持快速随机访问。不能添加或删除元素。 |
string | 与vector相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度快。 |
迭代器
迭代器范围:begin
到end
,即第一个元素到最后一个元素的后面一个位置(one past the last element)。
左闭合区间:$ [begin, end) $
左闭合范围蕴含的编程设定:
- 如果
begin
和end
相等,则范围为空。 - 如果二者不等,则范围至少包含一个元素,且
begin
指向该范围中的第一个元素。 - 可以对
begin
递增若干次,使得begin == end
。
获取迭代器
操作 | 解释 |
---|---|
c.begin(), c.end() | 返回指向c 的首元素和尾元素之后位置的迭代器 |
c.cbegin(), c.cend() | 返回const_iterator |
reverse_iterator | 按逆序寻址元素的迭代器 |
const_reverse_iterator | 不能修改元素的逆序迭代器 |
c.rbegin(), c.rend() | 返回指向c 的尾元素和首元素之前位置的迭代器 |
c.crbegin(), c.crend() | 返回const_reverse_iterator |
- 以
c
开头的版本是C++11新标准引入的 - 当不需要写访问时,应该使用
cbegin
和cend
。 - 不支持
forward_list
在向容器添加元素后:
- 如果容器是
vector
或string
,且存储空间被重新分配,则指向容器的迭代器、指针、引用都会失效。 - 对于
deque
,插入到除首尾位置之外的任何位置都会导致指向容器的迭代器、指针、引用失效。如果在首尾位置添加元素,迭代器会失效,但指向存在元素的引用和指针不会失效。 - 对于
list
和forward_list
,指向容器的迭代器、指针和引用依然有效。
在从一个容器中删除元素后:
- 对于
list
和forward_list
,指向容器其他位置的迭代器、引用和指针仍然有效。 - 对于
deque
,如果在首尾之外的任何位置删除元素,那么指向被删除元素外其他元素的迭代器、指针、引用都会失效;如果是删除deque
的尾元素,则尾后迭代器会失效,但其他不受影响;如果删除的是deque
的头元素,这些也不会受影响。 - 对于
vector
和string
,指向被删元素之前的迭代器、引用、指针仍然有效。 - 注意:当我们删除元素时,尾后迭代器总是会失效。
- 注意:使用失效的迭代器、指针、引用是严重的运行时错误!
- 建议:将要求迭代器必须保持有效的程序片段最小化。
- 建议:不要保存
end
返回的迭代器。
类型
操作 | 解释 |
---|---|
iterator | 此容器类型的迭代器类型 |
const_iterator | 可以读取元素但不能修改元素的迭代器类型 |
size_type | 无符号整数类型,足够保存此种容器类型最大可能的大小 |
difference_type | 带符号整数类型,足够保存两个迭代器之间的距离 |
value_type | 元素类型 |
reference | 元素的左值类型;和value_type & 含义相同 |
const_reference | 元素的const 左值类型,即const value_type & |
容器定义和初始化
操作 | 解释 |
---|---|
C c; | 默认构造函数,构造空容器 |
C c1(c2);或C c1 = c2; | 构造c2 的拷贝c1 |
C c(b, e) | 构造c ,将迭代器b 和e 指定范围内的所有元素拷贝到c |
C c(a, b, c…) | 列表初始化c |
C c(n) | 只支持顺序容器,且不包括array ,包含n 个元素,这些元素进行了值初始化 |
C c(n, t) | 包含n 个初始值为t 的元素 |
- 只有顺序容器的构造函数才接受大小参数,关联容器并不支持。
array
具有固定大小。- 和其他容器不同,默认构造的
array
是非空的。 - 直接复制:将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。
- 使用迭代器复制:不要求容器类型相同,容器内的元素类型也可以不同。
赋值和swap
操作 | 解释 |
---|---|
c1 = c2; | 将c1 中的元素替换成c2 中的元素 |
c1 = {a, b, c…} | 将c1 中的元素替换成列表中的元素(不适用于array ) |
c1.swap(c2) | 交换c1 和c2 的元素 |
swap(c1, c2) | 等价于c1.swap(c2) |
c.assign(b, e) | 将c 中的元素替换成迭代器b 和e 表示范围中的元素,b 和e 不能指向c 中的元素 |
c.assign(il) | 将c 中的元素替换成初始化列表il 中的元素 |
c.assign(n, r) | 将c 中的元素替换为n 个值是t 的元素 |
- 使用非成员版本的
swap
是一个好习惯(非成员版本的swap在泛型编程中非常重要)。 assign
操作不适用于关联容器和array
,仅顺序容器有效。- 除array外,swap不对任何元素进行拷贝,删除或插入操作。
大小
操作 | 解释 |
---|---|
c.size() | 容器中元素的数目(不支持forward_list ) |
c.max_size() | 容器中可保存的最大元素数目 |
c.empty() | 若容器中存储了元素,返回false ,否则返回true |
顺序容器添加元素
操作 | 解释 |
---|---|
c.push_back(t) | 在容器尾部创建一个值为t 的元素,返回void |
c.emplace_back(args) | 同上 |
c.push_front(t) | 在容器头部创建一个值为t 的元素,返回void |
c.emplace_front(args) | 同上 |
c.insert(p, t) | 在迭代器p 指向的元素之前创建一个值是t 的元素,返回指向新元素的迭代器 |
c.emplace(p, args) | 同上 |
c.inset(p, n, t) | 在迭代器p 指向的元素之前插入n 个值为t 的元素,返回指向第一个新元素的迭代器;如果n 是0,则返回p |
c.insert(p, b, e) | 将迭代器b 和e 范围内的元素,插入到p 指向的元素之前;如果范围为空,则返回p |
c.insert(p, il) | il 是一个花括号包围中的元素值列表,将其插入到p 指向的元素之前;如果il 是空,则返回p |
- 向vector,string或deque插入元素会使所有指向容器的迭代器,引用和指针失效。
- 因为这些操作会改变大小,因此不适用于
array
。 forward_list
有自己专有版本的insert
和emplace
。forward_list
不支持push_back
和emplace_back
。- 当我们用一个对象去初始化容器或者将对象插入到容器时,实际上放入的是对象的拷贝。
emplace
开头的函数是新标准引入的,这些操作是构造而不是拷贝元素。- 传递给
emplace
的参数必须和元素类型的构造函数相匹配。
访问元素
操作 | 解释 |
---|---|
c.back() | 返回容器中尾元素的引用。若容器为空,函数行为未定义 |
c.front() | 返回容器中头元素的引用。若容器为空,函数行为未定义 |
c[n] | 返回容器中下标是n 的元素的引用,n 时候一个无符号证书。若n>=c.size() ,则函数行为未定义 |
c.at(n) | 返回下标为n 的元素引用。如果下标越界,则抛出out_of_range 异常 |
- 访问成员函数返回的是引用。
at
和下标操作只适用于string
、vector
、deque
、array
。back
不适用于forward_list
。- 如果希望下标是合法的,可以使用
at
函数。
删除元素
操作 | 解释 |
---|---|
c.pop_back() | 删除容器中尾元素,若容器为空,则函数行为未定义。函数返回void |
c.pop_front() | 删除容器中首元素,若容器为空,则函数行为未定义。函数返回void |
c.erase(p) | 删除迭代器p 指向的元素,返回一个指向被删除元素之后的元素的迭代器,若p 本身是尾后迭代器,则函数行为未定义 |
c.erase(b, e) | 删除迭代器b 和e 范围内的元素,返回指向最后一个被删元素之后元素的迭代器,若e 本身就是尾后迭代器,则返回尾后迭代器 |
c.clear() | 删除容器中所有元素,返回void |
- 会改变容器大小,不适用于
array
。 forward_list
有特殊版本的erase
forward_list
不支持pop_back
vector
和string
不支持pop_front
特殊的forwad_list操作
- 链表在删除元素时需要修改前置节点的内容,双向链表会前驱的指针,但是单向链表没有保存,因此需要增加获取前置节点的方法。
forward_list
定义了before_begin
,即首前(off-the-begining)迭代器,允许我们再在首元素之前添加或删除元素。
操作 | 解释 |
---|---|
lst.before_begin() | 返回指向链表首元素之前不存在的元素的迭代器,此迭代器不能解引用。 |
lst.cbefore_begin() | 同上,但是返回的是常量迭代器。 |
lst.insert_after(p, t) | 在迭代器p 之后插入元素。t 是一个对象 |
lst.insert_after(p, n, t) | 在迭代器p 之后插入元素。t 是一个对象,n 是数量。若n 是0则函数行为未定义 |
lst.insert_after(p, b, e) | 在迭代器p 之后插入元素。由迭代器b 和e 指定范围。 |
lst.insert_after(p, il) | 在迭代器p 之后插入元素。由il 指定初始化列表。 |
emplace_after(p, args) | 使用args 在p 之后的位置,创建一个元素,返回一个指向这个新元素的迭代器。若p 为尾后迭代器,则函数行为未定义。 |
lst.erase_after(p) | 删除p 指向位置之后的元素,返回一个指向被删元素之后的元素的迭代器,若p 指向lst 的尾元素或者是一个尾后迭代器,则函数行为未定义。 |
lst.erase_after(b, e) | 类似上面,删除对象换成从b 到e 指定的范围。 |
改变容器大小
操作 | 解释 |
---|---|
c.resize(n) | 调整容器的大小为n 个元素,若n<c.size() ,则多出的元素被丢弃。若必须添加新元素,对新元素进行值初始化 |
c.resize(n, t) | 调整容器的大小为n 个元素,任何新添加的元素都初始化为值t |
vector分配原理
vector
和string
在内存中是连续保存的,如果原先分配的内存位置已经使用完,则需要重新分配新空间,将已有元素从就位置移动到新空间中,然后添加新元素。
操作 | 解释 |
---|---|
c.shrink_to_fit() | 将capacity() 减少到和size() 相同大小 |
c.capacity() | 不重新分配内存空间的话,c 可以保存多少个元素 |
c.reverse(n) | 分配至少能容纳n 个元素的内存空间 |
- shrink_to_fit只适用于vector、string和deque。
- capacity和reverse只适用于vector和string。
额外的string操作
构造string的其他方法
操作 | 解释 |
---|---|
string s(cp, n) | s 是cp 指向的数组中前n 个字符的拷贝,此数组 |
string s(s2, pos2) | s 是string s2 从下标pos2 开始的字符的拷贝。若pos2 > s2.size() ,则构造函数的行为未定义。 |
string s(s2, pos2, len2) | s 是string s2 从下标pos2 开始的len2 个字符的拷贝。 |
- n,len,pos2都是无符号值。
substr操作
操作 | 解释 |
---|---|
s.substr(pos, n) | 返回一个string ,包含s 中从pos 开始的n 个字符的拷贝。pos 的默认值是0,n 的默认值是s.size() - pos ,即拷贝从pos 开始的所有字符。 |
改变string的其他方法
操作 | 解释 |
---|---|
s.insert(pos, args) | 在pos 之前插入args 指定的字符。pos 可以使是下标或者迭代器。接受下标的版本返回指向s 的引用;接受迭代器的版本返回指向第一个插入字符的迭代器。 |
s.erase(pos, len) | 删除从pos 开始的len 个字符,如果len 被省略,则删除后面所有字符,返回指向s 的引用。 |
s.assign(args) | 将s 中的字符替换成args 指定的字符。返回一个指向s 的引用。 |
s.append(args) | 将args 指定的字符追加到s ,返回一个指向s 的引用。 |
s.replace(range, args) | 删除s 中范围range 中的字符,替换成args 指定的字符。返回一个指向s 的引用。 |
string搜索操作
string
类提供了6个不同的搜索函数,每个函数都有4个重载版本。- 每个搜索操作都返回一个
string::size_type
值,表示匹配发生位置的下标。如果搜索失败则返回一个名为string::npos
的static
成员(类型是string::size_type
,初始化值是-1,也就是string
最大的可能大小)。
搜索操作 | 解释 |
---|---|
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_first_not_of(args) | 在容器中查找最后一个不在args 中的字符 |
args必须是一下的形式之一:
args 形式 |
解释 |
---|---|
c, pos | 从容器中位置pos 开始查找字符c 。pos 默认是0 |
s2, pos | 从容器中位置pos 开始查找字符串s 。pos 默认是0 |
cp, pos | 从容器中位置pos 开始查找指针cp 指向的以空字符结尾的C风格字符串。pos 默认是0 |
cp, pos, n | 从容器中位置pos 开始查找指针cp 指向的前n 个字符。pos 和n 无默认值。 |
s.compare的几种参数形式
逻辑类似于C标准库的strcmp
函数,根据s
是等于、大于还是小于参数指定的字符串,s.compare
返回0、正数或负数。
参数形式 | 解释 |
---|---|
s2 | 比较s 和s2 |
pos1, n1, s2 | 比较s 从pos1 开始的n1 个字符和s2 |
pos1, n1, s2, pos2, n2 | 比较s 从pos1 开始的n1 个字符和s2 |
cp | 比较s 和cp 指向的以空字符结尾的字符数组 |
pos1, n1, cp | 比较s 从pos1 开始的n1 个字符和cp 指向的以空字符结尾的字符数组 |
pos1, n1, cp, n2 | 比较s 从pos1 开始的n1 个字符和cp 指向的地址开始n2 个字符 |
string和数值转换
转换 | 解释 |
---|---|
to_string(val) | 一组重载函数,返回数值val 的string 表示。val 可以使任何算术类型。对每个浮点类型和int 或更大的整型,都有相应版本的to_string() 。和往常一样,小整型会被提升。 |
stoi(s, p, b) | 返回s 起始子串(表示整数内容)的数值,p 是s 中第一个非数值字符的下标,默认是0,b 是转换所用的基数。返回int |
stol(s, p, b) | 返回long |
stoul(s, p, b) | 返回unsigned long |
stoll(s, p, b) | 返回long long |
stoull(s, p, b) | 返回unsigned long long |
stof(s, p) | 返回s 起始子串(表示浮点数内容)的数值,p 是s 中第一个非数值字符的下标,默认是0。返回float |
stod(s, p) | 返回double |
stold(s, p) | 返回long double |
容器适配器(adapter)
- 适配器是使一事物的行为类似于另一事物的行为的一种机制,例如
stack
可以使任何一种顺序容器以栈的方式工作。 - 初始化
deque<int> deq; stack<int> stk(deq);
从deq
拷贝元素到stk
。 - 创建适配器时,指定一个顺序容器,可以覆盖默认的基础容器:
stack<string, vector<string> > str_stk;
。
适配器的通用操作和类型
操作 | 解释 |
---|---|
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) | 同上 |
stack
操作 | 解释 |
---|---|
s.pop() | 删除栈顶元素,不返回。 |
s.push(item) | 创建一个新元素,压入栈顶,该元素通过拷贝或移动item 而来 |
s.emplace(args) | 同上,但元素由args 来构造。 |
s.top() | 返回栈顶元素,不删除。 |
- 定义在
stack
头文件中。 stack
默认基于deque
实现,也可以在list
或vector
之上实现。
queue和priority_queue
操作 | 解释 |
---|---|
q.pop() | 删除队首元素,但不返回。 |
q.front() | 返回队首元素的值,不删除。 |
q.back() | 返回队尾元素的值,不删除。只适用于queue |
q.top() | 返回具有最高优先级的元素值,不删除。 |
q.push(item) | 在队尾压入一个新元素。 |
q.emplace(args) |
- 定义在
queue
头文件中。 queue
默认基于deque
实现,priority_queue
默认基于vector
实现。queue
可以在list
或vector
之上实现,priority_queue
也可以用deque
实现。
泛型算法
因为它们实现共同的操作,所以称之为“算法”;而“泛型”、指的是它们可以操作在多种容器类型上。
泛型算法本身不执行容器操作,只是单独依赖迭代器和迭代器操作实现。
1 |
|
2 |
|
大多数算法是通过遍历两个迭代器标记的一段元素来实现其功能。
必要的编程假定:算法永远不会改变底层容器的大小。算法可能改变容器中保存的元素的值,也可能在容器内移动元素,但不能直接添加或者删除元素。
1 | vector<int>::const_iterator result = find(vec.begin(), vec.end(), search_value); |
输入:两个标记范围的迭代器和目标查找值。
返回:如果找到,返回对应的迭代器,否则返回第二个参数,即标记结尾的迭代器。
只读算法
- 只读取范围中的元素,不改变元素。
- 如
find
和accumulate
(在numeric
中定义,求和)。 find_first_of
,输入:两对迭代器标记两段范围,在第一段中找第二段中任意元素,返回第一个匹配的元素,找不到返回第一段的end
迭代器。- 通常最好使用
cbegin
和cend
。 equal
:确定两个序列是否保存相同的值。
写容器元素的算法
一些算法将新值赋予序列中的元素。算法不检查写操作(假定目的位置足够大)。
1 | fill(vec.begin(), vec.end(), 0); //将每个元素重置为0 |
2 | fill_n(vec.begin(), 10, 0); |
插入迭代器back_inserter
,接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。通过此迭代器赋值时,赋值运算符会调用push_back
将一个具有给定值的元素添加到容器中。
1 |
|
2 | |
3 | vector<int> vec; |
4 | auto it = back_inserter(vec); |
5 | *it = 42; |
6 | fill_n(back_inserter(vec), 10, 0); |
- 拷贝算法
copy
: - 输入:前两个参数指定输入范围,第三个指向目标序列。
copy (ilst.begin(), ilst.end(), back_inserter(ivec));
copy
时必须保证目标目的序列至少要包含与输入序列一样多的元素。
重排容器元素的算法
排序算法sort
,接受两个迭代器,表示要排序的元素范围。
消除重复unique
:
- 之前要先调用
sort
- 返回的迭代器指向最后一个不重复元素之后的位置。
- 顺序会变,重复的元素被“删除”。
- 并没有真正删除,真正删除必须使用容器操作。
1 | void elimDups(vector<string> &words) |
2 | { |
3 | sort(words.begin(), words.end()); |
4 | auto end_unique = unique(words.begin(), words.end()); |
5 | words.erase(end_unique, words.end()); |
6 | } |
定制操作
向算法传递函数:
谓词(predicate)是一个可调用的表达式,返回结果是一个能用作条件的值。一元谓词接受一个参数,二元谓词接受两个参数。
1 | bool isShorter(const string &s1, const string &s2) |
2 | { |
3 | return s1.size() < s2.size(); |
4 | } |
5 | stable_sort(words.begin(), words.end(), isShorter); |
lambda表达式
lambda表达式表示一个可调用的代码单元,可以理解成是一个未命名的内联函数。
1 | [capture list](parameter list) -> return type {function body} |
- capture list捕获列表是一个lambda所在函数定义的局部变量的列表(通常为空),不可忽略。
- return type是返回类型,lambda必须使用尾置返回形式,可忽略。
- parameter是参数列表,可忽略。
- function body是函数体,不可忽略。
- 可以忽略参数列表和返回类型,但必须包含捕获列表和函数体。
1 | auto f = [] {return 42;} |
2 | cout << f() <<endl; |
lambda不能有默认参数,实参与形参类型必须匹配且数目相等。
lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在lambda函数体中使用该变量。
捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和在它所在函数之外声明的名字。
1 | void biggies(vector<string> &words, |
2 | vector<string>::size_type sz) |
3 | { |
4 | elimDups(words); |
5 | stable_sort(words.begin(), words.end(), |
6 | [](const string &a, const string &b) |
7 | { return a.size() < b.size(); }); |
8 | |
9 | auto wc = find_if(words.begin(), words.end(), |
10 | [sz](const string &a) |
11 | { return a.size() >= sz; }); |
12 | |
13 | auto count = words.end() - wc; |
14 | cout << count << " " << make_plural(count, "word", "s") |
15 | << " of length " << sz << " or longer" << endl; |
16 | |
17 | for_each(wc, words.end(), |
18 | [](const string &s) {const << s << " ";}); |
19 | cout << endl; |
20 | } |
lambda捕获和返回
定义lambda
时,编译器会生成一个与lambda对应的新的(未命名的)类类型。当向一个函数传递一个lambda时,同时定义了一个新类型和该类型的一个对象:传递的参数就是此编译器生成的类类型的未命名对象。
默认情况下,从lambda生成的类都包含一个对应该lambda所捕获的变量的数据成员,在lambda对象创建时被初始化。
值捕获:被捕获的变量是可以拷贝,且被捕获的变量是在lambda创建时拷贝,而不是调用时拷贝。
1 | void fcn1() |
2 | { |
3 | size_t v1 = 42; //局部变量 |
4 | auto f = [v1] { return v1; }; |
5 | v1 = 0; |
6 | auto j = f(); //j为42,f保存了我们创建它时v1的拷贝 |
7 | } |
引用捕获:必须保证在lambda执行时,变量是存在的,当函数返回一个lambda,此lambda也不能包含引用捕获。
1 | void fcn2() |
2 | { |
3 | size_t v1 = 42; |
4 | auto f2 = [&v1] {return v1;}; |
5 | v1 = 0; |
6 | auto j = f2(); // j为0 |
7 | } |
尽量减少捕获的数据量,尽可能避免捕获指针或引用。
隐式捕获:让编译器推断捕获列表,在捕获列表中写一个&
(引用捕获方式)或=
(值捕获方式), 也可以混合使用隐式捕获和显示捕获。
1 | void biggies(vector<string> &words, vector<string>::size_type sz, |
2 | ostream &os = cout, char c = ' ') |
3 | { |
4 | for_each(words.begin(), words.end(), |
5 | [&, c](const string &s) { os << s << c; }); |
6 | |
7 | for_each(words.begin(), words.end(), |
8 | [=, &os](const string &s) { os << s << c; }); |
9 | } |
当混合使用隐式和显示捕获时,捕获列表中的第一个元素必须是&或=。此符号指定了默认捕获方式。
lambda捕获列表:
捕获列表 | 解释 |
---|---|
[] | 空捕获列表。lambda 不能使用所在函数中的变量。一个lambda 只有在捕获变量后才能使用它们。 |
[names] | names 是一个逗号分隔的名字列表,这些名字都是在lambda 所在函数的局部变量,捕获列表中的变量都被拷贝,名字前如果使用了& ,则采用引用捕获方式。 |
[&] | 隐式捕获列表,采用引用捕获方式。lambda 体中所使用的来自所在函数的实体都采用引用方式使用。 |
[=] | 隐式捕获列表,采用值捕获方式。 |
[&, identifier_list] | identifier_list 是一个逗号分隔的列表,包含0个或多个来自所在函数的变量。这些变量采用值捕获方式,而任何隐式捕获的变量都采用引用方式捕获。identifier_list 中的名字前面不能使用& |
[=, identifier_list] | identifier_list 中的变量采用引用方式捕获,而任何隐式捕获的变量都采用值方式捕获。identifier_list 中的名字不能包括this ,且前面必须使用& |
默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上关键字mutable
。因此,可变lambda能省略参数列表。
1 | void fcn3() |
2 | { |
3 | size_t v1 = 42; |
4 | auto f = [v1] () mutable { return ++v1; }; |
5 | v1 = 0; |
6 | auto j = f(); //j==43 |
7 | } |
默认情况下,如果一个lambda体包含return之外的任何语句,则编译器假定此lambda返回void。被推断为void的lambda不能返回值。当我们需要为一个lambda定义返回类型时,必须使用尾置返回类型。
1 | transform(v1.begin(), v1.end(), v1.begin(), |
2 | [](int i) ->int { if(i < 0) return -i; else return i; }); |
参数绑定
lambda
表达式更适合在一两个地方使用的简单操作。如果是很多地方使用相同的操作,还是需要定义函数。
标准库bind
函数:定义在头文件functional
中,可以看做为一个通用的函数适配器。接受一个可调用对象,生成一个新的可调用对象来”适应”原对象的参数列表。
1 | auto newCallable = bind(callable, arg_list); |
调用newCallable的时候,newCallable会调用callable并传递给它arg_list中的参数。arg_list中的参数可能包含形如_n
的名字,其中n是一个整数,这些参数表示占位符的意思, 表示newCallable的参数,占据了传递给newCallable的参数的”位置”。数值n表示生成的可调用对象中参数的位置: _1为newCallable的第一个参数, _2为第二个参数,以此类推。
名字_n定义在placeholders的命名空间中。
1 | using namespace std::placeholders; |
1 | auto g = bind(f, a, b, _2, c, _1); |
2 | //g(_1, _2) = f(a, b, _2, c, _1) |
非占位符的参数要使用引用传参,必须使用标准库ref
函数或者cref
(const引用类)函数。
1 | for_each(words.begin(), words.end(), |
2 | bind(print, ref(os), _1, ' ')); |
再探迭代器
插入迭代器
插入器是一种迭代器适配器,接受一个容器,生成一个迭代器,能实现向给定容器添加元素。
- back_inserter创建一个使用
push_back
的迭代器。 - front_inserter创建一个使用
push_front
的迭代器。 - inserter创建一个使用
insert
的迭代器。接受第二个参数,即一个指向给定容器的迭代器,元素会被查到迭代器所指向的元素之前。
插入迭代器操作
操作 | 解释 |
---|---|
it=t | 在it 指定的当前位置插入值t 。假定c 是it 绑定的容器,依赖于插入迭代器的不同种类,此赋值会分别调用c.push_back(t) 、c.push_front(t) 、c.insert(t, p) ,其中p 是传递给inserter 的迭代器位置 |
*it, ++it, it++ | 这些操作虽然存在,但不会对it 做任何事情,每个操作都返回it |
iostream迭代器
迭代器可与输入或输出流绑定在一起,用于迭代遍历所关联的 IO 流。通过使用流迭代器,我们可以用泛型算法从流对象中读取数据以及向其写入数据。
istream_iterator的操作
操作 | 解释 |
---|---|
istream_iterator |
in 从输入流is 读取类型为T 的值 |
istream_iterator |
读取类型是T 的值的istream_iterator 迭代器,表示尾后位置 |
in1 == in2 | in1 和in2 必须读取相同类型。如果他们都是尾后迭代器,或绑定到相同的输入,则两者相等。 |
in1 != in2 | 类似上条 |
*in | 返回从流中读取的值 |
in->mem | 与*(in).mem 含义相同 |
++in, in++ | 使用元素类型所定义的>> 运算符从流中读取下一个值。前置版本返回一个指向递增后迭代器的引用,后置版本返回旧值。 |
ostream_iterator的操作:
操作 | 解释 |
---|---|
ostream_iterator |
out 将类型为T 的值写到输出流os 中 |
ostream_iterator |
out 将类型为T 的值写到输出流os 中,每个值后面都输出一个d 。d 指向一个空字符结尾的字符数组。 |
out = val | 用<< 运算符将val 写入到out 所绑定的ostream 中。val 的类型必须和out 可写的类型兼容。 |
*out, ++out, out++ | 这些运算符是存在的,但不对out 做任何事情。每个运算符都返回out 。 |
反向迭代器
反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。
对于反向迭代器,递增和递减的操作含义会颠倒。
实现向后遍历,配合rbegin
和rend
。
泛型算法结构
5类迭代器
迭代器类别 | 解释 | 支持的操作 |
---|---|---|
输入迭代器 | 只读,不写;单遍扫描,只能递增 | == ,!= ,++ ,* ,-> |
输出迭代器 | 只写,不读;单遍扫描,只能递增 | ++ ,* |
前向迭代器 | 可读写;多遍扫描,只能递增 | == ,!= ,++ ,* ,-> |
双向迭代器 | 可读写;多遍扫描,可递增递减 | == ,!= ,++ ,-- ,* ,-> |
随机访问迭代器 | 可读写,多遍扫描,支持全部迭代器运算 | == ,!= ,< ,<= ,> ,>= ,++ ,-- ,+ ,+= ,- ,-= ,* ,-> ,iter[n] ==*(iter[n]) |
算法的形参模式
1 | alg(beg, end, other args); |
2 | alg(beg, end, dest, other args); |
3 | alg(beg, end, beg2, other args); |
4 | alg(beg, end, beg2, end2, other args); |
其中,alg
是算法名称,beg
和end
表示算法所操作的输入范围。dest
、beg2
、end2
都是迭代器参数,是否使用要依赖于执行的操作。
算法命名规范
- 一些算法使用重载形式传递一个谓词。
- 接受一个元素值的算法通常有一个不同名的版本:加
_if
,接受一个谓词代替元素值。 - 区分拷贝元素的版本和不拷贝的版本:拷贝版本通常加
_copy
。
特定容器算法
- 对于
list
和forward_list
,优先使用成员函数版本的算法而不是通用算法。
list和forward_list成员函数版本的算法
操作 | 解释 |
---|---|
lst.merge(lst2) | 将来自lst2 的元素合并入lst ,二者都必须是有序的,元素将从lst2 中删除。 |
lst.merge(lst2, comp) | 同上,给定比较操作。 |
lst.remove(val) | 调用erase 删除掉与给定值相等(==)的每个元素 |
lst.remove_if(pred) | 调用erase 删除掉令一元谓词为真的每个元素 |
lst.reverse() | 反转lst 中元素的顺序 |
lst.sort() | 使用< 排序元素 |
lst.sort(comp) | 使用给定比较操作排序元素 |
lst.unique() | 调用erase 删除同一个值的连续拷贝。使用== 。 |
lst.unique(pred) | 调用erase 删除同一个值的连续拷贝。使用给定的二元谓词。 |
上面的操作都返回void
list和forward_list的splice**成员函数版本的参数:
参数 | 解释 |
---|---|
(p, lst2) | p 是一个指向lst 中元素的迭代器,或者一个指向flst 首前位置的迭代器。函数将lst2 中的所有元素移动到lst 中p 之前的位置或是flst 中p 之后的位置。将元素从lst2 中删除。lst2 的类型必须和lst 相同,而且不能是同一个链表。 |
(p, lst2, p2) | 同上,p2 是一个指向lst2 中位置的有效的迭代器,将p2 指向的元素移动到lst 中,或将p2 之后的元素移动到flst 中。lst2 可以是于lst 或flst 相同的链表。 |
(p, lst2, b, e) | b 和e 表示lst2 中的合法范围。将给定范围中的元素从lst2 移动到lst 或first 中。lst2 与lst 可以使相同的链表,但p 不能指向给定范围中的元素。 |
使用lst.splice(args)
或flst.splice_after(args)
关联容器
关联容器和顺序容器的不同:关联容器中的元素时按照关键字来保存和访问的。
关联容器支持通过关键字来高效地查找和读取元素,基本的关联容器类型是 map
和 set
。
关联容器类型:
容器类型 | 解释 |
---|---|
按顺序存储 | |
map | 关键数组:保存关键字-值 对 |
set | 关键字即值,即只保存关键字的容器 |
multimap | 支持同一个键多次出现的map |
multiset | 支持同一个键多次出现的set |
无序集合 | |
unordered_map | 用哈希函数组织的map |
unordered_set | 用哈希函数组织的set |
unordered_multimap | 哈希组织的map ,关键字可以重复出现 |
unordered_multiset | 哈希组织的set ,关键字可以重复出现 |
关联容器概述
定义关联容器
需要指定元素类型。
列表初始化:
关键字类型的要求
对于有序容器,关键字类型必须定义元素比较的方法。默认是<
。
如果想传递一个比较的函数,可以这样定义:multiset<Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);
pair
在utility
头文件中定义。
一个pair
保存两个数据成员,两个类型不要求一样。
pair的操作:
操作 | 解释 |
---|---|
pair<T1, T2> p; | p 是一个pair ,两个类型分别是T1 和T2 的成员都进行了值初始化。 |
pair<T1, T2> p(v1, v2); | first 和second 分别用v1 和v2 进行初始化。 |
pair<T1, T2>p = {v1, v2}; | 等价于`p(v1, v2) |
make_pair(v1, v2); | pair 的类型从v1 和v2 的类型推断出来。 |
p.first | 返回p 的名为first 的数据成员。 |
p.second | 返回p 的名为second 的数据成员。 |
p1 relop p2 | 运算关系符按字典序定义。 |
p1 == p2 | 必须两对元素两两相等 |
p1 != p2 | 同上 |
关联容器操作
关联容器额外的类型别名
类型别名 | 解释 |
---|---|
key_type | 此容器类型的关键字类型 |
mapped_type | 每个关键字关联的类型,只适用于map |
value_type | 对于map ,是pair<const key_type, mapped_type> ; 对于set ,和key_type 相同。 |
关联容器迭代器
解引用一个关联容器迭代器时,会得到一个类型为容器的value_type
的值的引用。
set
的迭代器是const
的。
遍历关联容器:使用begin
和end
,遍历map
、multimap
、set
、multiset
时,迭代器按关键字升序遍历元素。
添加元素
关联容器insert
操作
insert 操作 |
关联容器 |
---|---|
c.insert(v) c.emplace(args) | v 是value_type 类型的对象;args 用来构造一个元素。 对于map 和set ,只有元素的关键字不存在c 中才插入或构造元素。函数返回一个pair ,包含一个迭代器,指向具有指定关键字的元素,以及一个指示插入是否成功的bool 值。对于multimap 和multiset 则会插入范围中的每个元素。 |
c.insert(b, e) c.insert(il) | b 和e 是迭代器,表示一个c::value_type 类型值的范围;il 是这种值的花括号列表。函数返回void 。对于 map 和set ,只插入关键字不在c 中的元素。 |
c.insert(p, v) c.emplace(p, args) | 类似insert(v) ,但将迭代器p 作为一个提示,指出从哪里开始搜索新元素应该存储的位置。返回一个迭代器,指向具有给定关键字的元素。 |
向map
添加元素:
1 | word_count.insert({word, 1}); |
2 | word_count.insert(make_pair(word, 1)); |
3 | word_count.insert(pair<string, size_t>(word, 1)); |
4 | word_count.insert(map<string, size_t>::value_type (word, 1)); |
删除元素
从关联容器中删除元素
操作 | 解释 |
---|---|
c.erase(k) | 从c 中删除每个关键字为k 的元素。返回一个size_type 值,指出删除的元素的数量。 |
c.erase(p) | 从c 中删除迭代器p 指定的元素。p 必须指向c 中一个真实元素,不能等于c.end() 。返回一个指向p 之后元素的迭代器,若p 指向c 中的尾元素,则返回c.end() |
c.erase(b, e) | 删除迭代器对b 和e 所表示范围中的元素。返回e 。 |
下标操作
map
和unordered_map
的下标操作
操作 | 解释 |
---|---|
c[k] | 返回关键字为k 的元素;如果k 不在c 中,添加一个关键字为k 的元素,对其值初始化。 |
c.at(k) | 访问关键字为k 的元素,带参数检查;若k 不存在在c 中,抛出一个out_of_range 异常。 |
查找元素
在一个关联容器中查找元素
操作 | 解释 |
---|---|
c.find(k) | 返回一个迭代器,指向第一个关键字为k 的元素,若k 不在容器中,则返回尾后迭代器 |
c.count(k) | 返回关键字等于k 的元素的数量。对于不允许重复关键字的容器,返回值永远是0或1。 |
c.lower_bound(k) | 返回一个迭代器,指向第一个关键字不小于k 的元素。 |
c.upper_bound(k) | 返回一个迭代器,指向第一个关键字大于k 的元素。 |
c.equal_range(k) | 返回一个迭代器pair ,表示关键字等于k 的元素的范围。若k 不存在,pair 的两个成员均等于c.end() 。 |
lower_bound
和upper_bound
不适用于无序容器。
下标和at
操作只适用于非const
的map
和unordered_map
。
无序容器
有序容器使用比较运算符来组织元素;无序容器使用哈希函数和关键字类型的==
运算符。
理论上哈希技术可以获得更好的性能。
无序容器在存储上组织为一组桶(bucket),每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。
无序容器管理操作
操作 | 解释 |
---|---|
桶接口 | |
c.bucket_count() | 正在使用的桶的数目 |
c.max_bucket_count() | 容器能容纳的最多的桶的数目 |
c.bucket_size(n) | 第n 个桶中有多少个元素 |
c.bucket(k) | 关键字为k 的元素在哪个桶中 |
桶迭代 | |
local_iterator | 可以用来访问桶中元素的迭代器类型 |
const_local_iterator | 桶迭代器的const 版本 |
c.begin(n),c.end(n) | 桶n 的首元素迭代器 |
c.cbegin(n),c.cend(n) | 与前两个函数类似,但返回const_local_iterator 。 |
哈希策略 | |
c.load_factor() | 每个桶的平均元素数量,返回float 值。 |
c.max_load_factor() | c 试图维护的平均比桶大小,返回float 值。c 会在需要时添加新的桶,以使得load_factor<=max_load_factor |
c.rehash(n) | 重组存储,使得bucket_count>=n ,且bucket_count>size/max_load_factor |
c.reverse(n) | 重组存储,使得c 可以保存n 个元素且不必rehash 。 |
动态内存与智能指针
对象的生命周期:
- 全局对象在程序启动时分配,结束时销毁。
- 局部对象在进入程序块时创建,离开块时销毁。
- 局部
static
对象在第一次使用前分配,在程序结束时销毁。 - 动态分配对象:只能显式地被释放。
对象的内存位置:
- 静态内存用来保存局部
static
对象、类static
对象、定义在任何函数之外的变量。 - 栈内存用来保存定义在函数内的非
static
对象。 - 堆内存,又称自由空间,用来存储动态分配的对象。
动态内存与智能指针
动态内存管理:
new
:在动态内存中为对象分配空间并返回一个指向该对象的指针。delete
:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。
智能指针:
- 管理动态对象。
- 行为类似常规指针。
- 负责自动释放所指向的对象。
- 智能指针也是模板。
shared_ptr类
shared_ptr和unique_ptr都支持的操作
操作 | 解释 |
---|---|
shared_ptr |
空智能指针,可以指向类型是T 的对象 |
p | 将p 用作一个条件判断,若p 指向一个对象,则为true |
*p | 解引用p ,获得它指向的对象。 |
p->mem | 等价于(*p).mem |
p.get() | 返回p 中保存的指针,要小心使用,若智能指针释放了对象,返回的指针所指向的对象也就消失了。 |
swap(p, q) p.swap(q) | 交换p 和q 中的指针 |
shared_ptr独有的操作
操作 | 解释 |
---|---|
make_shared |
返回一个shared_ptr ,指向一个动态分配的类型为T 的对象。使用args 初始化此对象。 |
shared_ptr |
p 是shared_ptr q 的拷贝;此操作会递增q 中的计数器。q 中的指针必须能转换为T* |
p = q | p 和q 都是shared_ptr ,所保存的指针必须能互相转换。此操作会递减p 的引用计数,递增q 的引用计数;若p 的引用计数变为0,则将其管理的原内存释放。 |
p.unique() | 若p.use_count() 是1,返回true ;否则返回false |
p.use_count() | 返回与p 共享对象的智能指针数量;可能很慢,主要用于调试。 |
使用动态内存的三种原因:
- 程序不知道自己需要使用多少对象(比如容器类)。
- 程序不知道所需要对象的准确类型。
- 程序需要在多个对象间共享数据。
直接管理内存
用new
动态分配和初始化对象。
new
无法为分配的对象命名(因为自由空间分配的内存是无名的),因此是返回一个指向该对象的指针。int *pi = new int(123);
- 一旦内存耗尽,会抛出类型是
bad_alloc
的异常。
用delete
将动态内存归还给系统。
- 接受一个指针,指向要释放的对象。
delete
后的指针称为空悬指针(dangling pointer)。
使用new
和delete
管理动态内存存在三个常见问题:
- 1.忘记
delete
内存。 - 2.使用已经释放掉的对象。
- 3.同一块内存释放两次。
坚持只使用智能指针可以避免上述所有问题。
shared_ptr和new结合使用
定义和改变shared_ptr的其他方法
操作 | 解释 |
---|---|
shared_ptr |
p 管理内置指针q 所指向的对象;q 必须指向new 分配的内存,且能够转换为T* 类型 |
shared_ptr |
p 从unique_ptr u 那里接管了对象的所有权;将u 置为空 |
shared_ptr |
p 接管了内置指针q 所指向的对象的所有权。q 必须能转换为T* 类型。p 将使用可调用对象d 来代替delete 。 |
shared_ptr |
p 是shared_ptr p2 的拷贝,唯一的区别是p 将可调用对象d 来代替delete 。 |
p.reset() | 若p 是唯一指向其对象的shared_ptr ,reset 会释放此对象。若传递了可选的参数内置指针q ,会令p 指向q ,否则会将p 置空。若还传递了参数d ,则会调用d 而不是delete 来释放q 。 |
p.reset(q) | 同上 |
p.reset(q, d) | 同上 |
智能指针和异常
如果使用智能指针,即使程序块由于异常过早结束,智能指针类也能确保在内存不需要的时候将其释放。
智能指针陷阱:
- 不用相同的内置指针初始化(或
reset
)多个智能指针 - 不
delete get()
返回的指针。 - 如果你使用
get()
返回的指针,记得当最后一个对应的智能指针销毁后,你的指针就无效了。 - 如果你使用智能指针管理的资源不是
new
分配的内存,记住传递给它一个删除器。
unique_ptr
某一个时刻只能有一个unique_ptr
指向一个给定的对象。
不支持拷贝或者赋值操作。
向后兼容:auto_ptr
:老版本,具有unique_ptr
的部分特性。特别是,不能在容器中保存auto_ptr
,也不能从函数返回auto_ptr
。
unique_ptr操作
操作 | 解释 |
---|---|
unique_ptr |
空unique_ptr ,可以指向类型是T 的对象。u1 会使用delete 来是释放它的指针。 |
unique_ptr<T, D> u2 | u2 会使用一个类型为D 的可调用对象来释放它的指针。 |
unique_ptr<T, D> u(d) | 空unique_ptr ,指向类型为T 的对象,用类型为D 的对象d 代替delete |
u = nullptr | 释放u 指向的对象,将u 置为空。 |
u.release() | u 放弃对指针的控制权,返回指针,并将u 置空。 |
u.reset() | 释放u 指向的对象 |
u.reset(q) | 令u 指向q 指向的对象 |
u.reset(nullptr) | 将u 置空 |
weak_ptr
weak_ptr
是一种不控制所指向对象生存期的智能指针。
指向一个由shared_ptr
管理的对象,不改变shared_ptr
的引用计数。
一旦最后一个指向对象的shared_ptr
被销毁,对象就会被释放,不管有没有weak_ptr
指向该对象。
weak_ptr操作
操作 | 解释 |
---|---|
weak_ptr |
空weak_ptr 可以指向类型为T 的对象 |
weak_ptr |
与shared_ptr 指向相同对象的weak_ptr 。T 必须能转换为sp 指向的类型。 |
w = p | p 可以是shared_ptr 或一个weak_ptr 。赋值后w 和p 共享对象。 |
w.reset() | 将w 置为空。 |
w.use_count() | 与w 共享对象的shared_ptr 的数量。 |
w.expired() | 若w.use_count() 为0,返回true ,否则返回false |
w.lock() | 如果expired 为true ,则返回一个空shared_ptr ;否则返回一个指向w 的对象的shared_ptr 。 |
动态数组
new和数组
new
一个动态数组:
- 类型名之后加一对方括号,指明分配的对象数目(必须是整型,不必是常量)。
- 返回指向第一个对象的指针。
int *p = new int[size];
delete
一个动态数组:
delete [] p;
unique_ptr
和数组:
- 指向数组的
unique_ptr
不支持成员访问运算符(点和箭头)。
操作 | 解释 |
---|---|
unique_ptr<T[]> u | u 可以指向一个动态分配的数组,整数元素类型为T |
unique_ptr<T[]> u(p) | u 指向内置指针p 所指向的动态分配的数组。p 必须能转换为类型T* 。 |
u[i] | 返回u 拥有的数组中位置i 处的对象。u 必须指向一个数组。 |
allocator类
- 标准库
allocator
类定义在头文件memory
中,帮助我们将内存分配和对象构造分离开。 - 分配的是原始的、未构造的内存。
allocator
是一个模板。allocator<string> alloc;
标准库allocator类及其算法:
操作 | 解释 |
---|---|
allocator |
定义了一个名为a 的allocator 对象,它可以为类型为T 的对象分配内存 |
a.allocate(n) | 分配一段原始的、未构造的内存,保存n 个类型为T 的对象。 |
a.deallocate(p, n) | 释放从T* 指针p 中地址开始的内存,这块内存保存了n 个类型为T 的对象;p 必须是一个先前由allocate 返回的指针。且n 必须是p 创建时所要求的大小。在调用deallocate 之前,用户必须对每个在这块内存中创建的对象调用destroy 。 |
a.construct(p, args) | p 必须是一个类型是T* 的指针,指向一块原始内存;args 被传递给类型为T 的构造函数,用来在p 指向的内存中构造一个对象。 |
a.destroy(p) | p 为T* 类型的指针,此算法对p 指向的对象执行析构函数。 |
allocator伴随算法
操作 | 解释 |
---|---|
uninitialized_copy(b, e, b2) | 从迭代器b 和e 给定的输入范围中拷贝元素到迭代器b2 指定的未构造的原始内存中。b2 指向的内存必须足够大,能够容纳输入序列中元素的拷贝。 |
uninitialized_copy_n(b, n, b2) | 从迭代器b 指向的元素开始,拷贝n 个元素到b2 开始的内存中。 |
uninitialized_fill(b, e, t) | 在迭代器b 和e 执行的原始内存范围中创建对象,对象的值均为t 的拷贝。 |
uninitialized_fill_n(b, n, t) | 从迭代器b 指向的内存地址开始创建n 个对象。b 必须指向足够大的未构造的原始内存,能够容纳给定数量的对象。 |
- 定义在头文件
memory
中。 - 在给定目的位置创建元素,而不是由系统分配内存给他们。