《C++ Primer》阅读笔记——第17章 标准库特殊设施

17.1 tuple类型

  • 不同tuple类型的成员类型也不相同,但一个tuple可以有任意数量的成员
  • 每个确定的tuple类型的成员数目是固定的,但一个tuple类型的成员数目可以与另一个tuple类型不同
  • 将一些数据组合成单一对象

17.1.1 定义和初始化tuple

  • 当定义一个tuple对象时,需要指出每个成员的类型
  • tuple的默认构造函数会对每个成员进行值初始化
  • 也可以为每个成员提供初始值,这个构造函数是explicit的,必须使用直接初始化语法
  • make_tuple函数使用初始值的类型来推断tuple的类型
  • 访问tuple的成员
    • 一个tuple类型的成员数目是没有限制的
    • get标准库函数模板,尖括号中的值必须是一个整型常量表达式
    • 如果不知道一个tuple准确的类型细节信息,可以使用两个辅助类模板来查询tuple成员的数量和类型
size_t sz = tuple_size<trans>::value; // tuple中数据个数
tuple_element<1, trans>::type cnt = get<1>(item); //cnt的类型是tuple中第1个元素对应的类型
  • 关系和相等运算符
    • 只有两个tuple具有相同数量的成员时,才可以比较它们

    • 为了使用相等或不等运算符,对每对成员使用==必须都是合法的

    • 为了使用关系运算符,对每对成员使用<必须都是合法的

    • 由于tuple定义了< 和 == 运算符,因此可以将tuple序列传递给算法,并且可以在无序容器中将tuple作为关键字类型

17.1.2 使用tuple返回多个值

  • 返回tuple的函数

    • equal_range算法,返回一个迭代器pair,表示元素的范围;如果两个迭代器相等表示空范围;否则第一个迭代器指向第一条匹配的记录,第二个迭代器指向匹配的尾后位置
  • 使用函数返回的tuple

17.2 bitset类型

17.2.1 定义和初始化bitset

  • bitset类是一个类模板,具有固定的大小,当我们定义一个bitset时,需要声明它包含多少个二进制位

    • 大小必须是一个常量表达式
    • 编号从0开始的二进制位称为低位,编号到31结束的二进制位被称为高位
  • 用unsigned值初始化bitset

    • 当使用一个整型值来初始化bitset时,该值被转换为unsigned long long类型并被当作位模式处理
  • 从一个string初始化bitset

    • 当使用字符串表示数时,字符串中下标最小的字符对应高位,反之亦然

17.2.2 bitset操作

  • 函数size是一个constexpr函数,因此可以在任何要球常量表达式的地方使用

  • 下标运算符对const属性进行了重载

    • const版本的下标运算符在指定位置位时返回true
    • 非const版本返回bitset定义的一个特殊类型,允许操作指定位的值
  • 提取bitset的值

    • to_ulong、to_ullong
    • 如果bitset中的值不能放入给定类型中,则这两个操作会抛出一个overflow_error异常
  • bitSet的IO运算符

    • 输入运算符从一个输入流读取字符,保存到一个临时的string对象中。直到读取的字符数达到对应bitset大小时,或是遇到不是1或0的字符时,或是遇到文件尾或输入错误时,读取过程才停止。随即用临时string对象来初始化bitset
  • 使用bitset

17.3 正则表达式

  • C++正则表达式库(RE库),定义在头文件regex中
  • 函数regex_match 和 regex_search 确定一个给定字符序列与一个给定regex是否匹配。如果整个输入序列与表达式匹配,则regex_match函数返回true;如果输入序列中一个子串与表达式匹配,则regex_search函数返回true

17.3.1 使用正则表示式库

  • 默认情况下,regex使用的正则表达式语言是ECMAScript

  • 指定regex对象的选项

    • regex的构造函数和赋值操作可能抛出类型为regex_error的异常
  • 指定或使用正则表达式时的错误

    • 正则表达式是在运行时,当一个regex对象被初始化或被赋予一个新模式时,才被”编译“的
    • 也就是说,一个正则表示的语法是否正确是在运行时解析的
    • 如果正则表达式存在错误,则在运行时标准库会抛出一个类型为regex_error的异常
    • 正则表达式的编译是一个非常慢的操作,所以应该避免创建很多不必要的regex
  • 正则表达式类和输入序列类型

    • 使用RE库类型必须与输入序列类型匹配

17.3.2 匹配与Regex迭代器类型

  • regex迭代器是一种迭代器适配器,被绑定到一个输入序列和一个regex对象上

  • 使用sregex_iterator

    • 空的sregex_iterator可以起到尾后迭代器的作用
  • 使用匹配数据

    • 匹配类型有两个名为prefix 和 suffix的成员,分别返回表示输入序列中当前匹配之前和之后部分的ssub_match对象。
    • 一个ssub_match对象有两个名为str和length的成员,分别返回匹配的string和该string的大小

17.3.3 使用子表达式

  • 正则表达式中的模式通常包含一个或多个子表达式。一个子表达式是模式的一部分,本身也具有意义

  • 正则表达式的语法通常用括号表示子表达式

  • 每当我们用括号分组多个可行选项时,同时也就声明了这些选项形成子表达式

  • 匹配对象除了提供匹配整体的相关信息外,还提供访问模式中每个子表达式的能力。子匹配是按位置来访问的。第一个子匹配位置为0,表示整个模式对应的匹配,随后是每个子表达式对应的匹配

  • 子表达式用于数据验证

    • 验证必须匹配特定格式的数据
  • 使用子匹配操作

17.3.4 使用regex_replace

  • 在输入序列中查找并替换一个正则表达式

  • 只替换输入序列的一部分

  • 用来控制匹配和格式的标志

    • 匹配和格式化标志的类型为match_flag_type 定义在名为regex_constants的命名空间中
    • std :: regex_constants ::
  • 使用格式标志

    • 默认情况下,regex_replace输出整个输入序列,未与正则表达式匹配的部分会原样输出;匹配的部分按照格式字符串指定的格式输出
    • format_no_copy:不输出输入序列中未匹配的部分

17.4 随机数

  • rand函数存在的问题:

    • 转换rand生成的随机数的范围、类型或分布时,常常会引入非随机性
  • 随机数引擎类 和 随机数分布类

    • 一个引擎类可以生成unsigned 随机数序列
    • 一个分布类使用一个引擎类生成指定类型的、在给定范围内的、服从特定概率分布的随机数

17.4.1 随机数引擎和分布

  • 随机数引擎是函数对象类,定义了一个调用运算符,该运算符不接受参数并返回一个随机unsigned整数

  • 分布类型和引擎

    • 分布类型也是函数对象类
    • 同样定义了一个调用运算符,接受一个随机数引擎作为参数
    • 使用它的引擎参数生成随机数,并将其映射到指定的分布
    • 随机数发生器 = 分布对象 + 引擎对象
uniform_int_distribution<unsigned> u(0, 9);
default_random_engine e;
u(e);
  • 比较随机数引擎和rand函数

    • 随机数引擎生成的unsigned整数在一个系统定义的范围内
    • rand函数生成的数的范围在0到RAND_MAX之间
  • 引擎生成一个数值序列

    • 即使生成的数看起来是随机的,但对一个给定的发生器,每次运行程序它都会返回相同的数值序列
    • 可以将引擎和关联的分布对象定义为static的
      • 从而每次调用都会生成新的数
      • 在函数调用之间会保持住状态
  • 设置随机数发生种子

    • 为引擎设置种子有两种方式:

      • 在创建引擎对象时提供种子
      • 调用引擎的seed成员
    • 调用系统函数time

      • 返回以秒计的时间,因此这种方法只适用于生成种子的间隔为秒级或更长的应用

17.4.2 其他随机数分布

  • 生成随机实数

    • 最常用的方式:rand()的结果除以RAND_MAX

      • 这种方法其实不正确
      • 随机整数的精度通常低于随机浮点数,有一些浮点值永远不会被生成了
    • 可以使用uniform_real_distribution类型的对象

  • 使用默认分布的默认结果类型

    • 分布类型都是模板,具有单一的模板类型参数,表示分布生成的随机数的类型
    • 每个分布模板都有一个默认模板实参
  • 生成非均匀分布的随机数

    • normal_distribution 正态分布
    • lround()函数,将每个随机数舍入到最接近的整数
  • bernoulli_distribution类

    • 该分布不接受模板参数,是一个普通类,而非模板
    • 总是返回一个bool值,返回true的概率是一个常数,默认值是0.5
    • 由于引擎返回相同的随机数序列,所以必须在循环外声明引擎对象,否则每步循环都会创建一个新引擎,从而每步循环都会生成相同的值。类似的,分布对象也要保持状态,因此也应该在循环外定义
    • bernoulli_distribution是可以调整概率的
bernoulli_distribution b(.55);

17.5 IO库再探

  • 格式控制、未格式化IO和随机访问

17.5.1 格式化输入与输出

  • 每个iostream对象维护着一个格式状态来控制IO如何格式化细节

  • 标准库定义了一组操纵符,来修改流的格式状态

    • 一个操纵符是一个函数或是一个对象,会影响流的状态,并能用作输入或输出运算符的运算对象
  • 很多操纵符改变格式状态

    • 操纵符用于两大类输出控制:

      • 控制数值的输出形式
      • 控制补白的数量和位置
    • 当操纵符改变流的格式状态时,通常改变后的状态对所有后续IO都生效

      • 因此最好在不再需要特殊格式时尽快将流恢复到默认状态
  • 控制布尔值的格式

    • boolalpha操纵符
      • 打印布尔值的操作变成打印true或false,而非1或0
      • 取消时使用noboolalpha
  • 指定整型值的进制

    • 可以使用操纵符hex、oct、dec改为十六进制、八进制、十进制
    • 只会影响整型运算对象,浮点值的表示形式不受影响
  • 在输出中指出进制

    • showbase操纵符 在输出结果中显示进制
    • noshowbase恢复
    • uppercase 字母输出大写
  • 控制浮点数格式

    • 默认情况下,浮点值按照六位数字精度打印;如果没有小数部分,则不打印小数点;根据浮点数的值选择打印成定点十进制或科学计数法形式

    • 指定打印精度

      • 打印时,浮点值按照当前精度舍入而非截断
      • 可以通过precision成员或使用setprecision操纵符来改变精度
    • 指定浮点数计数法

      • 操纵符scientific 使用科学计数法
      • 操纵符fixed使用定点十进制
      • 操纵符hexfloat 强制使用十六进制格式
      • 操纵符defaultfloat 将流恢复到默认状态
      • 在执行scientific、fixed、hexfloat后,精度值控制的是小数点后面的数字位数,而默认情况下精度值指定的是数字的总位数
    • 打印小数点

      • 操纵符showpoint
  • 输出补白

  • 控制输入格式

    • 操纵符noskipws令输入运算符读取空白符 skipws恢复

17.5.2 未格式化的输入/输出操作

  • 允许将一个流当作一个无解释的字节序列来处理

  • 单字节操作

    • 读取而不是忽略空白符
  • 将字符放回输入流

    • 三种方法:

      • peek返回输入流中下一个字符的副本,但不会将它从流中删除,peek返回的值仍然留在流中
      • unget使得输入流向后移动,从而最后读取的值又回到流中,即使不知道最后从流中读出了什么值,仍然可以调用unget
      • putback 更特殊版本的unget,退回从流中读取的最后一个值,接受一个参数,此参数必须与最后读取的值相同
    • 一般情况下在读取下一个值之前,标准库保证我们可以退回最多一个值,标准库不保证在中间不进行读取操作的情况下能连续调用putback或unget

  • 从输入操作返回的int值

    • 以int类型从输入流返回一个字符
      • 可以返回文件尾标记(char范围中的每个值都表示一个字符,没有额外的值来表示文件尾)
      • 将要返回的字符先转换为unsigned char,然后再将结果提升到int
      • 使用负值表示文件尾
  • 多字节操作

    • get将分隔符留作istream中的下一个字符
    • getline则读取并丢弃分隔符
  • 确定读取了多少个字符

    • gcount 最后一个未格式化输入操作读取了多少个字符
    • 应该在任何后续未格式化输入操作之前调用gcount

17.5.3 流随机访问

  • 随机IO本质上是依赖于系统的

  • 在大多数系统上,绑定到cin、cout、cerr和clog的流不支持随机访问

  • istream ostream 通常不支持随机访问

  • seek 和 tell函数

    • seek:通过将标记seek到一个给定位置来重定位它

    • tell:tell标记的当前位置

    • 标准库实际上定义了两对seek 和 tell 函数,分别用于输入流和输出流

  • 只有一个标记

    • 在一个流中只维护单一的标记,并不存在独立的读标记和写标记
    • fstream 和 stringstream 可以读写同一个流, 由单一的缓冲区用于保存读写的数据
      • 标记只有一个,用于表示缓冲区中的当前位置
      • 标准库将g和p版本都映射到这个单一的标记
      • 由于只有单一标记,因此要在读写间切换,就必须进行seek操作来重定位标记
  • 重定位标记

  • 访问标记

    • tell函数通常用来记住一个位置,以便稍后再定位回来
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值