第十七章 标准库特殊设施
17.1 tuple类型
- 定义在tuple头文件中
- tuple是类似pair的模板
- 一个tuple可以有任意数量的成员,每个确定的tuple类型的成员数目是固定的,但一个tuple类型的成员数目可以与另一个tuple类型不同.
17.1.1 定义和初始化tuple
当我们定义一个tuple时候,需要指出每个成员的类型
tuple <size_t, size_t, size_t> threeD; //默认值初始化为0
tuple 的构造函数时explicit的,因此我们必须使用直接初始化语法
//生成tuple对象
auto i = make_tuple("string", 3, 2.3);
访问tuple的成员
不能像pair一样用first,second等,因为tuple类型的成员数目是没有限制的,使用 get 的标准库函数模板
传递给get一个tuple对象,返回指定成员的引用
//尖括号中的值必须是常量表达式,返回成员的引用, 0是第一个成员,1是第二个,依次类推
auto book = get<0>(i);
//返回成员数量
auto s = tuple_size<i>::value;
//使用成员的类型 这里面tuple_element<1, i>::type代表int类型
tuple_element<1, i>::type cnt = get<1>(i);
关系和相等运算符
只有两个tuple具有相等数量的成员时,才可以进行比较,两个成员使用==或<等运算符必须是合法的
17.1.2 使用tuple返回多个值
tuple常见的用途就是一个函数返回多个值
17.2 bitset类型
用于位运算,在头文件bitset中
17.2.1 定义和初始化bitset
bitset类是一个类模板,指定大小,大小需要时常量表达式
bitset<32> bitvec(1U);
bitset中的二进制位是未命名的,我们通过位置来访问他们,二进制位的位置是从0开始编号的,编号从0开始的二进制被称为低位,编号到最大值的二进制位称为高位
用unsigned值初始化bitset
当我们使用一个整型值来初始化bitset时,此值将被转换为unsigned long long类型并被当作位模式来处理,bitset中的二进制位是此模式的一个副本
- 如果bitset大小大于一个unsigned long long , 则剩余高位置为0
- 如果bitset大小小于一个unsigned long long , 则超出部分被丢弃
从一个string初始化bitset
可以从一个string或一个字符数组指针来初始化bitset
- 字符串中下标最小的字符对应高位
- 注意string下标标号从左往右递增,而bitset是从右往左递增
17.2.2 bitset操作
bit.size()是一个常量表达式,可以用在常量表达式的地方.
- 我的理解:因为bitset指定大小的时候也需要传入一个常量表达式,因此在编译时候就知道了size,因此其也是个常量表达式
- 下标运算符对const属性进行了重载
- const版本的下标运算符在指定位 置位 时返回true,否则返回false
- 非const版本返回bitset定义的一个特殊类型,允许操纵指定位的值
提取bitset的值
to_ulong和to_ullong操作返回一个值,保存了与bitset对象相同的位模式,只有bitset的大小小于ul和ull两种类型的大小的时候才能使用这个操作,否则会抛出异常
bitset的IO运算符
输入运算符从一个输入流读取字符,保存到临时的string对象中,直到读取字符数达到bitset大小或者是遇到不是1或0的字符,或是遇到文件尾或输入错误时候才停止, 之后用string对象来初始化bitset.
17.3 正则表达式
正则表达式是一种描述字符序列的方法,是一种极其强大的计算工具.
正则表达式库(RE库),定义在头文件regex中.
17.3.1 使用正则表达式库
看完整章,主要用在了字符串的匹配工作/格式化工作
指定regex对象的选项
regex r(re,f)
//re是一个正则表达式,可以用string,迭代器等多种方式,f是一个标志
使用RE库的类型必须与输入序列的类型匹配
17.3.2 匹配与Regex迭代器类型
sregex_iterator
regex的迭代器是一种迭代器适配器,被绑定到一个输入序列和一个regex对象.
17.3.3 使用子表达式
正则表达式语法通常用括号表示子表达式
可单独访问每个子表达式,(序号),0表示整个表达式,1 表示第一个子表达式,一次类推
17.3.4 使用regex_replace
实现产找到给定的序列并替换为我们希望的另一个序列
可以设置控制配合和格式的标志
17.4 随机数
定义在头文件random中的随机数库通过一组协作的类来工作:
- 随机数引擎类: 生成随机unsigned整数序列
- 随机数分布类: 使用引擎返回服从特定概率分布的随机数
C++程序不应该使用rand(C库函数),而是使用default_random_engine类和恰当的分布类对象
17.4.1 随机数引擎和分布
标准库定义了多个随机数引擎类,区别是性能和随机性质量不同.
每个编译器会指定其中一个作为default_random_engine类型
随机数引擎的输出是不能够直接使用的,因为其范围通常与我们的需求不符
分布类型和引擎
分布类型定义了一个调用运算符,他接受一个随机数引擎作为参数,分布对象使用它的引擎参数生成随机数,并将其映射到指定分布.
我们所说的随机数发生器,其实指分布对象和引擎对象的组合
一个给定的随机数发生器一直会生成相同的随机数序列.一个函数如果定义了局部的随机数发生器,应该将其(包括引擎和分布对象)定义为static的.否则,每次调用函数都会生成相同的序列.
设置随机数发生器的种子
随机数发生器会生成相同的随机数序列这一特性在调试中很有用. 一旦我们的程序调试完毕,我们通常希望每次运行程序都会生成不同的随即结果.
可以通过提供一个 种子 来达到这个目的.
选取好的种子十分困呐,比较常用的方法是调用系统函数time,定义在ctime头文件中, time返回以秒计的时间,因此这种方式只适合生成种子的间隔位秒级或者更长的应用.
可以在初始化时候设置种子,也可以调用引擎的seed成员
17.4.2 其他随机数分布
生成随机实数
程序常需要一个随机浮点数的源,特别是经常需要0到1之间的随机数
新标库中,很容易获得随机浮点数,定义一个uniform_real_distribution类型对象
uniform_real_distribution<double> u(0,1);
使用分布的默认结果类型
每个分布模板都有一个默认模板实参,使用默认随机数类型是在模板名之后使用尖括号
uniform_real_distribution<> u(0,1); //使用默认double类型
生成非均匀分布的随机数
新标准库定义了20种分布类型.
- 正态分布
normal_distribution<>n(u,sigma);
均值和标准差
bernoulli_distribution类是一个普通类,不是模板类
- 此分布返回一个bool值,返回bool概率是一个常数,默认0.5
- 由于引擎返回相同的随机数序列,通常应该在循环外声明和定义引擎对象和分布对象
17.5 IO库再探
17.5.1 格式化输入与输出
标准库定义了一组操纵符来修改流的格式状态,一个操纵符是一个函数或是一个对象,会影响流的状态,可以用作输入输出运算符的运算对象。
很多操纵符改变格式状态
操纵符用于两大类输出控制:控制数值的输出形式以及控制补白的数量和位置。大多数改变格式状态的操纵符都是设置/复原成对的。
当操纵符改变流格式的状态时,通常对之后的IO都生效,因此在不需要特殊格式的时候尽快恢复到默认状态。
控制布尔值的格式
使用boolalpha 使得false,true输出打印而不是0 和 1
取消使用noboolalpha
指定整型值的进制
hex, oct, dec 分别表示十六进制,八进制,十进制
在输出中指定进制
使用showbase显式进制 , noshowbase不显示
- 前导0x十六进制
- 前导0八进制
- 无前导十进制
uppercase输出大写,nouppercase恢复
控制浮点数的格式
- 多高精度打印浮点值,就是有多少个数字(默认六位数字精度打印)
- 打印十六进制、定点十进制还是科学计数法形式 (标准库会选择可读性好的)
- 对于没有小数部分的浮点值是否打印小数点 (默认不打印)
1.指定打印精度
默认情况控制打印数字的总数,按照舍入规则。
IO对象的成员precision和setprecision操纵符
setprecision操纵符和其他接收参数的操纵符定义在头文件iomanip中
cout.precision() 和 cout.precision(int) 是重载的,一个返回当前精度,一个设置指定的精度返回当前精度
2.指定浮点数记数法
除非需要控制浮点数表示形式,否则最好有标准库选择记数法
- scientific使用科学计数法;
- fixed定点十进制;
- hexfloat是用十六进制格式;
- defaultfloat恢复默认状态
- 打印小数点
showpoint操纵符强制打印小数点noshowpoint恢复默认格式
输出补白
按列打印时候要控制数据来良好展示
- setw指定下一个数字或字符串值的最小空间,不改变输出流的内部状态,只决定下一个输出的大小.
- left左对齐
- right 右对齐 默认是右对齐
- internal 左对齐符号,右对齐值,中间空间空格填满
- setfill 使用一个默认字符代替空格补白
控制输入格式
默认情况,输入运算符会忽略空白符(空格\制表\换行\换纸\回车)
使用noskipws会令输入运算符读取空白符而不是跳过,skipws恢复默认行为
17.5.2 未格式化的输入/输出操作
标准库支持未格式化IO,允许我们将一个流当作一个无解释的字节序列来处理
单字节操作
is.get() is.get(ch)
os.put() 等可以每次一个字节地处理流
将字符放回输入流
有时候需要读取一个字符才能知道还未准备好处理它,希望将字符放回流中.有三种方法:
- peek 返回输入流中下一个字符的副本, 不会将它从流中删除
- unget 使得输入流向后移动,从而最后读取的值又回到流中
- putback(ch) 退回从路中读取的最后一个值,需要参数,参数值是读取的最后一个值
从输入操作返回int值
peek和无参get() 都以int类型返回一个字符,因为这样可以返回文件尾标记EOF
多字节操作
一次处理大块数据
get和getline参数相同时行为类似,区别是处理分隔符的方式,get将分隔符留在iostream中作为下一个字符,getline读取并丢弃分隔符
确定读取了多少个字符
调用gcount来确定最后一个未格式化输入操作读取了多少个字符
17.5.3 流随机访问
stream和ostream不支持流随机访问, 以下内容只适用于fstream,sstream
各种流类型通常都支持对流中数据的随机访问,可以重定位流,使之跳过一些数据,首先读取最后一行,然后读取第一行.
标准库提供了seek定位和tell当前位置的函数对
seek和tell函数
g版本和p版本,g是读取数据,p是写入数据
只有一个标记
一个流中只有单一的标记,p和g都使用这一个标记,因此在读写操作切换时候必须进行seek操作重定位标记
重定位标记
seek两个版本,一个移动到绝对位置,另一个移动到偏移位置
访问标记
tellp和tellg返回一个pos_type值,表示流当前位置
读写同一个文件
注意每次更改标记以及返回标记