第十七章:标准库特殊设施

第十七章:标准库特殊设施

一.tuple类型

tuple是类似pair的模板。不同tuple类型的成员类型不相同,一个tuple可以有任意数量的成员。每个确定的tuple类型的成员数目是固定的。

tuple类型及其伴随类型都定义在tuple中。

当我们定义一个tuple时,需要指出每个成员的类型。make_tuple根据初始值的类型来推断tuple的类型。要访问一个tuple的成员,就要使用一个名为get的标准库函数模板。

二.bitset类型

标准库定义了bitset类,使得位运算的使用更为容易,并且能够处理超过最长整型类型大小的位集合。

bitset是一个类模板,当我们定义bitset时,需要声明它包含多少个二进制位。编号从0开始的二进制位是低位。

当我们使用一个整型值来初始化bitset时,此值将被转换为unsigned long long类型并被当做位模式来处理。如果bitset的大小大于一个unsigned long long中的二进制位数,则剩余的高位被置为0,如果小于,则给定值中超过的高位将被丢弃。

我们可以从一个string或一个字符数组指针来初始化bitset。字符串中下标最小的字符对应高位。

bitset还支持一般的位运算符。

bitset的下标运算胡对const属性进行了重置,const版本的下标运算符在指定位置位时返回true,否则返回false。非const版本返回bitset定义的一个特殊类型,它允许我们操纵指定位的值。

三.正则表达式

正则表达式是一种描述字符序列的方法,是一种极其强大的计算工具。C++有一个正则表达式库RE库,RE库定义在头文件regex中,包含多个组件。

regex类表示一个正则表达式。函数regex_match和regex_search确定一个给定字符序列和一个给定regex是否匹配。

当我们定义一个regex或是对一个regex调用assign为其赋予新值时,可以指定一些标志来影响regex如何操作。例如我们有6个标志可以控制正则表达式的语法采用哪种标准。默认情况下,使用ECMAScript标志从而regex的正则表达式使用ECMA-262规范。

一个regex对象被初始化或被赋予一个新模式时,是在运行时才被“编译”的,所以一个正则表达式语法是否正确是在运行时才解析的。同时这意味着我们要避免创建不必要的正则表达式,因为在运行时解析正则表达式是一个非常慢的操作,特别是使用了扩展的正则表达式语法或复杂的正则表达式。因此构造一个regex对象以及先一个已存在的regex赋予一个新的正则表达式可能是非常耗时的。

我们使用的RE库类型必须与输入序列类型匹配,例如cmatch表示字符数组序列,smatch表示string类型序列,wsmatch表示wstring输入。

对于string我们可以用sregex_iterator来获取所有匹配。其他类型同理。

处理允许打印输入字符串中匹配的部分之外,匹配结果类还提供了有关匹配结果的更多细节信息。例如匹配结果的上下文。

正则表达式中的模式通常包含一个或多个子表达式,一个子表达式是模式的一部分,本身也具有意义。正则表达式通常用括号表示子表达式。匹配对象除了提供匹配整体的相关信息之外,还提供访问模式中每个子表达式的能力。子匹配是按位置来访问的。第一个子匹配位置为0,表示整个模式对应匹配,随后是每个子表达式的匹配,对应1、2…

子表达式的一个常见用途是验证必须匹配特定格式的数据。

当我们希望在输入序列中查找并替换一个正则表达式时,可以调用regex_replace。

四.随机数

在新标准出现之前,C和C++都依赖于一个简单的C库函数rand来生成随机数。此函数生成均匀分布伪随机整数,每个随机数的范围在0和一个系统相关的最大值之间。但是rand有一些问题,例如我们可能要随机浮点数,要均与分布的数,为了解决这些问题又常常引入了非随机性。

定义在头文件random中的丝技术库通过一组写作的类来解决这些问题:随机数引擎类和随机数分布类。一个引擎类可以生成unsigned随机数序列,一个分布类使用一个引擎类生成指定类型的、在给定范围内的、服从特定概率分布的随机数。

C++程序不应该使用库函数rand,而应使用default_random_engine类和恰当的分布类对象。

随机数引擎类是函数对象类,定义了调用运算符。该运算符不接受参数并返回一个随机unsigned整数。我们可以通过调用一个随机数引擎对象来生成原始随机数。

标准库定义了多个随机数引擎类,区别在于性能和随机性质量不同。每个编译器都会指定其中一个座位default_random_engine类型。

对于大多数场合,随机数引擎的输出是不能直接使用的,因为生成的随机数的值范围通常与我们需要的不符。

为了得到在一个指定范围内的数,我们使用一个分布类型的对象。分布类型也是函数对象类,分布类型定义了一个调用运算符,它接受一个随机数引擎作为参数。分布对象使用它的引擎参数生成随机数,并将其映射到指定的分布。注意我们传递的是引擎本身,而不是引擎生成的下一个值。

当我们说随机数发生器时,是指分布对象和引擎对象的组合。

一个给定的随机数发生器一直会生成相同的随机数序列。一个函数如果定义了局部的随机数发生器,**应该将其(包括引擎和分布对象)定义为static的。**否则,每次调用函数都会生成相同的序列。

随机数发生器会生成相同的随机数序列这一特性在调试中很有用。但是调试完毕后我们通常希望每次运行程序都会生成不同的随机结果。可以通过提供一个种子来达到这一目的。种子就是一个数值,引擎可以利用它从序列中一个新位置重新开始会生成随机数。

为引擎设置种子有两种方式:在创建引擎对象时提供种子;或者调用引擎的seed成员。

选择一个好的种子是十分困难的。最常用的方法是调用系统函数time。time返回一个特定时刻到当前经过了多少秒。函数time接受单个指针参数,它指向用于写入时间的数据结构。如果此指针为空,则函数简单地返回时间。由于time返回以秒计的时间,因此这种方式只适用于生成种子的间隔为秒级或更长的应用。

随机数引擎生成usigned数,范围内的每个数被生成的概率是相同的。而应用常常需要不同类型或不同分布的随机数。使用新标准库设施,可以很容易地获得随机浮点数。我们可以定义一个uniform_real_distribution类型的对象并让标准库来处理从随机整数到随机浮点数的映射。

分布类型都是模板,具有单一的模板参数,表示分布生成的随机数的类型。每个分布模板有一个默认模板实参。生成浮点值的分布类型默认生成double值,而生成整型值的分布默认生成int值。当我们希望使用默认随机数类型时记得在模板名之后使用空尖括号。

新标准库的另一个优势是可以生成非均匀分布的随机数。

有一个分布不接受模板参数,即bernoulli_distribution,因为它是一个普通类,而非模板。此分布总是返回一个bool值。它返回true的概率是一个常数为0.5(默认是0.5,也可以调整)。

五.IO库再探

除了条件状态外,每个iostream对象还维护一个格式状态来控制IO如何格式化的细节。

标准库定义一组操纵符来修改流的格式状态。一个操纵符是一个函数或是一个对象,会影响流的状态,并能用作输入或输出运算符的运算对象。

当操纵符改变流的格式状态时,通常改变后的状态对所有后续IO都生效。

到目前为止,我们只使用过格式化IO操作。输入和输出运算符根据读取或写入的数据类型来格式化它们。标准库还提供了一组低层操作,支持未格式化IO。这些操作允许我们将一个流当作一个无解释的字节序列来处理。如is.get(ch),os.put(ch),is.getline()等。

各种流类型通常都支持对流中数据的随机访问。我们可以重定位流,使之跳过一些数据。标准库提供了一对函数来定义和告诉流的位置。分别为seek和tell。

istream和ostrream不支持随机访问。

为了支持随机访问,IO类型维护一个标记来确定下一个读写操作要在哪里进行。它们还提供了两个函数,一个函数通过将标记seek到一个给定位置来重定位它;另一个函数tell我们标记的当前位置。标准库实际上定义了两对seek和tell函数,一对用于输入流,一对用于输出流,差别在于名字的后缀是g还是p。

虽然有seek和tell两个操作,但是标准库在一个流中只维护单一的标记。

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值