阿克苏网站建设,手机html网站开发工具,阿坝州网站制作,专业东莞网站建设报价引言
除了第2章介绍的内置类型之外,C语言还定义了 -个内容丰富的抽象数据类型库。其中,string和 vector是两种最重耍的标准库类型#xff0c;前者支持可变长字符串#xff0c;后者则 表示可变长的集合。还有…种标准库类型是迭代器#xff0c;它是string和vector的配套类型…引言
除了第2章介绍的内置类型之外,C语言还定义了 -个内容丰富的抽象数据类型库。其中,string和 vector是两种最重耍的标准库类型前者支持可变长字符串后者则 表示可变长的集合。还有…种标准库类型是迭代器它是string和vector的配套类型常被用于访问string中的字符或vector中的元素。内置数组是一种更基础的类型string和vector都是对它的某种抽象。本章将分别介绍数组以及标准摩类型string和 vector.第2 章介绍的内置类型是由C语言直接定义的。这些类型比如数字和字符体现了大多数计算机硬件本身具备的能力。标准库定义了另外一组具有更高级性质的类型它们尚未直接实现到计算机硬件中。本章将介绍两种最重要的标准库类型string和 vector。string表示可变长的 字符序列vector存放的是某种给定类型对象的可变长序列。本章还将介绍内置数组类型和其他内置类型一样数组的实现与硬件密切相关。因此相较于标准库类型string和vector,数组在灵活性上稍显不足。 在开始介绍标准库类型之前先来学习一种访问库中名字的简单方法
3.1命名空间的using声明
目前为止我们用到的库函数基本上都属于命名空间std,而程序也显式地将这一点标示了出来。例如std::cin表示从标准输入中读取内容。此处使用作用域操作符(::)(参见1.2节第7页)的含义是编译器应从操作符左侧名字所示的作用域中寻找右侧那个名字。因此std::cin的意思就是要使用命名空间std中的名字cin。上面的方法显得比较烦琐然而幸运的是通过更简单的途径也能使用到命名空间中的成员。本节将学习其中一种最安全的方法也就是使用using声明(usingdeclaration).18.2.2节(第702页)会介绍另一种方法。有了using声明就无须专门的前缀(形如命名空间)也能使用所需的名字了。using声明具有如下的形式using namespace::name; 一旦声明了上述语句就可以直接访问命名空间中的名字每个名字都需要独立的using声明
按照规定每个using声明引入命名空间中的一个成员。例如可以把要用到的标准库中的名字都以using声明的形式表示出来重写1.2节 (第 5 页)的程序如下在上述程序中一开始就有对cin、cout和 endl的 using声明这意味着我们不用再 添加std::形式的前缀就能直接使用它们。C语言的形式比较自由因此既可以一行只放一条using声明语句也可以一行放上多条。不过要注意用到的每个名字都必须有自己的声明语句而且每句话都得以分号结束。
头文件不应包含using声明
位于头文件的代码参见2.6.3节第67页一般来说不应该使用using声明。这是因为头文件的内容会拷贝到所有引用它的文件中去如果头文件里有某个using声明那么每个使用了该头文件的文件就都会有这个声明。对于某些程序来说由于不经意间包含了一些名字反而可能产生始料未及的名字冲突。
一点注意事项
经本节所述后面的所有例子将假设但凡用到的标准库中的名字都已经使用using语句声明过了。例如我们将在代码中直接使用cin,而不再使用std::cin?为了让书中的代码尽量简洁今后将不会再把所有using声明和#include指令一一标出。附录A中的表A.1第766页列出了本书涉及的所有标准库中的名字及对应的头文件。
3.2标准库类型string
标准库类型string表示可变长的字符序列使用string类型必须首先包含~84~]string头文件。作为标准库的一部分string定义在命名空间std中。接下来的示例都假定已包含了下述代码#include string using std::string;本节描述最常用的string操作9.5节 第320页还将介绍另外一些。
3 .2 .1 定义和初始化string对象
如何初始化类的对象是由类本身决定的。一个类可以定义很多种初始化对象的方式只不过这些方式之间必须有所区别或者是初始值的数量不同或者是初始值的类型不同。 表 3.1列出了初始化string对象最常用的一些方式下面是几个例子可以通过默认的方式(参见2.2.1节第 40页)初始化一个string对象这样就会得到 —个空的string,也就是说该string对象中没有任何字符。如果提供了一个字符串字面值(参见2.1.3节第 36页)则该字面值中除了最后那个空字符外其他所有的字符都被拷贝到新创建的string对象中去。如果提供的是一个数字和一个字符则string对象的内容是给定字符连续重复若干次后得到的序列直接初始化和拷贝初始化
由2.2.1节(第39页)的学习可知C语言有几种不同的初始化方式通过string我们可以清楚地看到在这些初始化方式之间到底有什么区别和联系。如果使用等号()初始化-个变量实际上执行的是拷贝初始化(copyinitialization),编译器把等号右侧的初始值拷贝到新创建的对象中去。与之相反如果不使用等号则执行的是直接初始化(directinitialization)。等号使用拷贝初始化无等号使用直接初始化当初始值只有一个时使用直接初始化或拷贝初始化都行。如果像上面的S4那样初始化要用到的值有多个一般来说只能使用直接初始化的方式3.2.2string对象上的操作
一个类除了要规定初始化其对象的方式外还要定义对象上所能执行的操作。其中类既能定义通过函数名调用的操作就像Sales_item类的isbn函数那样参见1.5.2节第20页也能定义、等各种运算符在该类知象上的新含义。表3.2中列举了string的大多数操作。读写string对象
第 1章曾经介绍过使用标准库中的iostream来读写int, double等内置类型的 值。同样也可以使用IO操作符读写string对象cins;//将string对象读入s,遇到空白停止coutsendl;//输出s
使用getline读取一整行
有时我们希望能在最终得到的字符串中保留输入时的空白符这时应该用getline函数代替原来的运算符。getline函数的参数是一个输入流和一个string对象函数从给定的输入流中读入内容直到遇到换行符为止(注意换行符也被读进来了)然后把所读的内容存入到那个string对象中去(注意不存换行符)。getline只要一遇到换行符就结束读取操作并返回结果哪怕输入的一开始就是换行符也是如此。如果输入真的一开始就是换行符那么所得的结果是个空string。和输入运算符一样getline也会返回它的流参数。因此既然输入运算符能作为判断的条件(参见1.4.3节第13页)我们也能用getline的结果作为条件。例如可以通过改写之前的程序让它一次输出一整行而不再是每行输出一个词了因为line中不包含换行符所以我们手动地加上换行操作符。和往常一样使用endl 结束当前行并刷新显示缓冲区。触发getline函数返回的那个换行符实际上被丢弃掉了得到的string对象中并不包含该换行符
string 的 empty 和 size 操作
顾名思义empty函数根据string对象是否为空返回一个对应的布尔值(参见第2.1节30页)。和Sales_item类(参见1.5.2节第20页)的isbn成员一样empty也是string的一个成员函数。调用该函数的方法很简单只要使用点操作符指明是哪个对象执行了empty函数就可以了。在上面的程序中if语句的条件部分使用了逻辑非运算符()它返回与其运算对象相反的结果。此例中如果str不为空则返回真。size函数返回string对象的长度(即string对象中字符的个数)可以使用size函数只输出长度超过80个字符的行string::size_type类型
对于size函数来说返回一个int或者如前面2.1.1节(第31页)所述的那样返回-个unsigned似乎都是合情合理的。但其实size函数返回的是一个string::size_type类型的值下面就对这种新的类型稍作解释。string类及其他大多数标准库类型都定义了几种配套的类型。这些配套类型体现了标准库类型与机器无关的特性类型size_type即是其中的一种。在具体使用的时候通过作用域操作符来表明名字size_type是在类string中定义的。尽管我们不太清楚string::size_type类型的细节但有一点是肯定的它是一个无符号类型的值(参见2.1.1节第30员)而且能足够存放下任何string对象的大小。所有用于存放string类的size函数返回值的变量都应该是string::size_type类型的。过去string::size_type这种类型有点儿神秘不太容易理解和使用。在C11新标准中允许编译器通过auto或者decltype(参见2.5.2节第61页)来推断变量的类型auto lenline.size();//len的类型是string::size_type由于size函数返回的是一个无符号整型数因此切记如果在表达式中混用了带符号数和无符号数将可能产生意想不到的结果(参见2.1.2节第33页)。例如假设n是一个具有负值的int,则表达式s.size()n的判断结果几乎肯定是true。这是因为负值n会自动地转换成一个比较大的无符号值。如果一条表达式中已经有了size()函数就不要再使用int了这样可以避免丁中混用int和unsigned可能带来的问题
比较string对象
string类定义了几种用于比较字符串的运算符。这些比较运算符逐一比较string对象中的字符并且对大小写敏感也就是说在比较时同一个字母的大写形式和小写形式是不同的。相等性运算符和!分别检验两个string对象相等或不相等string对象相等意味着它们的长度相同而且所包含的字符也全都相同。关系运算符、、、分别检验一个string对象是否小于、小于等于、大于、大于等于另外一个string对象。上述这些运算符都依照大小写敏感的字典顺序1.如果两个string对象的长度不同而且较短string对象的每个字符都与较长string对象对应位置上的字符相同就说较短string对象小于较长string对象。2,如果两个string对象在某些对应的位置上不一致则string对象比较的结果其实是string对象中第一对相异字符比较的结果两个string对象相加
两个string对象相加得到一个新的string对象其内容是把左侧的运算对象与右侧的运算对象串接而成。也就是说对string对象使用加法运算符的结果是一个新的string对象它所包含的字符由两部分组成前半部分是加号左侧string对象所含的字符、后半部分是加号右侧string对象所含的字符。另外复合赋值运算符参见1.4.1节第10页负责把右侧string对象的内容追加到左侧string对象的后面字面值和string对象相加
如2.1.2节第33页所讲的即使一种类型并非所需我们也可以使用它不过前提是该种类型可以自动转换成所需的类型。因为标准库允许把字符字面值和字符串字面值参见2.1.3节第36页转换成string对象所以在需要string对象的地方就可以使用这两种字面值来替代。利用这一点将之前的程序改写为如下形式string si nhelloM , s2 world; // 在 si 和 s2 中都没有标点符号string s3 si s2 f \nf ;当把string对象和字符字面值及字符串字面值混在一条语句中使用时必须确保每个加 法运算符( ) 的两侧的运算对象至少有一个是string用双引号包含起来的叫做字符串字面值 / 字面值等号两边至少一个需要 是string s4和 s5初始化时只用到了一个加法运算符因此很容易判断是否合法。s6的初始化形式之前没有出现过但其实它的工作机理和连续输入连续输出(参见1.2节第 6 页)是一样的可以用如下的形式分组string s6 (si , ) world;其中子表达式sl /的结果是一个string对象它同时作为第二个加法运算符的左侧运算对象因此上述语句和下面的两个语句是等价的很容易看到括号内的子表达式试图把两个字符串字面值加在一起而编译器根本没法做到这一点所以这条语句是错误的。因为某些历史原因也为了与C兼容所以C语言中的字符串字面值并不是标准库类型string的对象切记字符串字面值与string是不同的类型.
3.2.3处理string对象中的字符
我们经常需要单独处理string对象中的字符比如检查一个string对象是否包含空白或者把string对象中的字母改成小写再或者查看某个特定的字符是否出现等。这类处理的一个关键问题是如何获取字符本身。有时需要处理string对象中的每一个字符另外一些时候则只需处理某个特定的字符还有些时候遇到某个条件处理就要停下来。以往的经验告诉我们处理这些情况常常要涉及语言和库的很多方面。另一个关键问题是要知道能改变某个字符的特性。在 cctype头文件中定义了一组标准库函数处理这部分工作表 3.3列出了主要的函数名及其含义。建议使用C版本的C标准库头文件
C标准库中除了定义C语言特有的功能外也兼容了C语言的标准库。C语言的头文件形如name.h,C则将这些文件命名为cname。也就是去掉了.h后缀而在文件名mme之前添加了字母c,这里的c表示这是一个属于C语言标准库的头文件。因此cctype头文件和ctype.h头文件的内容是一样的只不过从命名规范上来讲更符合C语言的要求。特别的在名为cname头文件中定义的名字从属于命名空间std,而定义在名为.h的头文件中的则不然。一般来说C程序应该使用名cname的头文件而不使用name.h的形式标准库中的名字总能在命名空间std中找到。如果使用.h形式的头文件程序员就不得不时刻牢记哪些是从C语言那儿继承过来的哪些又是C语言所独有的。
使用范围for语句改变字符串中的字符
如果想要改变string对象中字符的值必须把循环变量定义成引用类型(参见2.3.1节第45页)。记住所谓引用只是给定对象的一个别名因此当使用引用作为循环控制变量时这个变量实际上被依次绑定到了序列的每个元素上。使用这个引用我们就能改变它绑定的字符。新的例子不再是统计标点符号的个数了假设我们想要把字符串改写为大写字母的形式。为了做到这一点可以使用标准库函数toupper,该函数接收一个字符然后输出其对应的大写形式。这样为了把整个string对象转换成大写只要对其中的每个字符调用toupper函数并将结果再赋给原字符就可以了每次迭代时变量c 引用string对象s 的下一个字符赋值给c 也就是在改变s 中对应字符的值。因此当执行下面的语句时c toupper (c) ; // c 是一个引用因此赋值语句将改变s 中字符的值实际上改变了 c 绑定的字符的值。整个循环结束后str中的所有字符都变成了大写形式。