这里是学习C++ primer做的笔记
将介绍两种重要的标准库类型:string和vector。string表示可变长的字符序列,vector存放的是某种给定类型对象的可变长序列。
命名空间using声明
作用域操作符::
的含义是:编译器应从操作符左侧名字所示的作用域中寻找右侧那个名字。因此,std::cin
的意思就是使用命名空间std中的名字cin。单只这种方法比较繁琐,本节使用一种最安全的方法,即使用using声明,有了using声明就无需专门的前缀,也能使用所需的名字了。using声明使用如下形式:
比如:
每个名字都需要独立的using声明,用到的每个名字都必须有自己的声明语句,而且每句话都得以分号结束。
头文件的代码一般来说不应该使用using声明,这是因为头文件的内容会拷贝到所有引用它的文件中,如果头文件里使用某个using声明,那么每个使用了该头文件的文件都会有这个声明,对于某些程序来说,反而会产生始料未及的名字冲突。
string
使用string都需要包含以下代码:
定义和初始化string对象
初始化string的几种方式:
上面如果使用等号(=)初始化一个变量,实际执行的是拷贝初始化,编辑器把等号右侧的初始化拷贝到新创建的对象中去,与之相反,如果不使用等号,则执行的是直接初始化。
string对象上的操作
在执行读取string操作时,string对象会自动忽略开头的空白,并从第一个真正的字符开始读起,直到遇见下一处空白为止。如下:
如果程序的输入是" Hello World!"
,则输出的将是"Hello"
,输出的结果中没有任何空格。
上面的代码如果输入" Hello World ! "
,输出的结果是"HelloWorld!"
使用getline
来读取一整行,可以保留输入时的空白符,参数是一个输入流和string对象,函数从给定的输入流中读入内容,直到遇到换行符为止。
getline函数返回的换行符实际上是被丢掉了的得到的结果中并不包含该换行符。
string的empty
根据string对象是否为空返回一个对应的布尔值,size
函数返回string对象的长度。
这里提一下size
函数,其返回类型是string::size_type
,它是一个无符号类型的值,因此在比较的时候就不要使用int类型了,比如如果n是一个具有负值的int,表达式s.size()<n
的结果肯定是true,这是因为负数n会自动转换为一个比较大的无符号值。
这里讲一下字面值和string对象相加,初始化string对象时,必须确保加法运算符的两侧有至少有一个string,而不都是字面值,也就是说不能把字面值直接相加
处理string对象中的字符
cctype头文件中的函数
如果想要改变string对象中的字符的值,在使用for循环中,必须把循环变量定义成引用类型。
标准库类型 vector
标准库类型vector表示对象的集合,其中所有对象的类型都相同,其被称为容器,使用vector,必须包含头文件,如下:
c++中既有类模板,也有函数模板,其中vector就是一个类模板。
定义和初始化vector对象
如下几种初始化vector的方法
向一个vector对象中添加元素
对于vector对象来说,直接初始化的方式适用于数量少,初始值是另一个vector对象的副本,所有元素的初始值都一样的情况,但是一般我们不知道实际需要的元素个数。这里使用成员函数push_back
向其中添加元素,负责把一个值当成vector对象的尾元素“压到”vector对象的“尾端”
其他vector操作
一些重要的操作
迭代器介绍
string和vector都支持迭代器,迭代器类拥有名为begin
和end
的成员,讲一下迭代器类型与const
由上,begin
和end
返回的具体类型由对象是否是常量决定,如果对象是常量,begin
和end
返回const_iterator
,如果对象不是常量,返回iterator
:
为了便于专门得到const_iterator
类型的返回值,C++11新标准引入两个新函数,分别是cbegin
和cend
,不管vector对象是什么,其返回值都是const_iterator
凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素
迭代器之间的关系运算(>,>=,<,<=)
的含义:比如某迭代器指向的容器在另一个迭代器所指位置之前,则说前者小于后者。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一个元素。
数组
与vector的区别在于数组的大小是确定不变的,对于某些特殊的应用来说程序运行时性能较好,但是相应的也损失了一些灵活性。
定义和初始化内置数组
初始化时数组维度应该是一个常量
默认情况下数组的元素被默认初始化,和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值。
显式初始化数组元素
字符数组中如果直接用字符串来赋值的话,注意字符串字符的结尾处还有一个空字符,会像其它字符串字符一样被拷贝到字符数组中。
数组中不允许赋值和拷贝
下面是比较复杂的数组声明
这里对于数组而言,由内向外阅读要比从右向左阅读好多了,举上面最后一个例子:首先是圆括号获取来的部分,*Parray
意味着Parray是个指针,接下来观察右边,可知道Parray是个指向大小为10的数组指针,最后观察左边,知道数组中的元素是int,这样即:Parray是一个指针,指向一个int数组,数组中包含10个元素。
再比如下面一句:
首先array是一个引用,是ptrs的引用,然后先跟[]
结合,表示是一个数组,再看*
,表示是一个指向int的指针(int *
结合)
访问数组元素
在使用数组下标的时候,通常将其定义为size_t
的类型,size_t
是一种机器相关的无符号类型,它被设计的足够大以便能表示内存中任意对象的大小。
指针和数组
对数组元素使用取地址符就能得到指向该元素的指针,在使用数组名的地方,编译器会自动将其替换为一个指向数组首元素的指针。
指针也是迭代器,vector和string的迭代器支持的运算,数组的指针全部支持。虽然如此,但是指针的迭代极易出错,为了让指针的使用更简单,安全,C++11引入两个名为begin
和end
的函数,这两个函数与容器中的两个同名成员功能类似,不过数组毕竟不是类类型,正确的使用方法是将数组作为它们的参数:
C风格字符串
下面是C风格字符串的函数,这里的p是指针,字符数组的首地址。
这里使用的时候传入此类函数的指针必须以空字符作为结束的数组:
strlen
函数将有可能沿着ca在内存中的位置不断的寻找,直到遇到空字符才停下来。
这里讲了C风格的字符数组和标准库string的对比,可以知道对于字符数组,在使用的时候,一旦所存得内容改变,就必须重新检查其空间是否足够,而且这类代码充满了风险,经常导致严重的安全漏洞。使用标准库string要比使用C风格字符串更安全,更高效。
与旧代码的接口
使用数组初始化vector对象:前面讲到不允许使用一个数组为另一个内置类型的数组赋初值,也不允许使用vector对象初始化数组,相反,允许使用数组来初始化vector对象。
上面代码中,用于创建ivec的两个指针实际上指明了用来初始化的值在数组arr中的位置,其中第二个指针应该是指向待拷贝区域尾元素的下一个位置。
现在的C++程序应该尽量使用vector和迭代器,避免使用内置数组和指针;应该尽量使用string,避免使用C风格的机遇数组的字符串。
完!(20180123)