前文介绍的内置类型是C++语言直接定义的。这些类型,比如数字和字符,体现了大多数计算机硬件本身具备的能力。标准库定义了另外一组具有更高级性质的类型,它们尚未直接实现到计算机硬件中。
- 通过 using namespace::name; 可以直接访问命名空间中的名字。位于头文件的代码一般来说不应该使用using声明,因为引用该头文件的文件很可能因此产生名字冲突。
- 标准库类型string表示可变长的字符序列,使用string必须包含string头文件,作为标准库的一部分,string定义在命名空间std中。
- string对象上的操作:
- os<<s 将s写到输出流os中,返回os
- is>>s 从is中读取字符串赋给s,字符串以空白符分隔,返回is,每次读到空白符停止(不读空白符)
- getline(is,s) 从is中读取一行赋给s(不存换行符但读换行符),返回is,
- s.empty() 为空返回true
- s.size() 返回字符个数
- s[n] 返回第n个字符的引用
- s1+s2 拼接
- s1=s2 用s2的副本代替s1原来的字符
- <,<=,>,>= 根据字典序比较
- 对于size函数来说,返回一个int或者unsigned都是合情合理的,但其实size函数返回的是一个string::size_type类型的值。string类及其他大多数标准库类型都定义了几种配套的类型,这些配套类型体现了标准库与机器无关的特性,类型size_type即是其中的一种。在具体使用的时候,通过作用域操作符来表示size_type是类string中定义的。可以肯定的是,该类型是一个无符号类型。所以尽量避免将其与int混用。
- 标准库允许将字符字面值和字符串字面值转换成string对象,所以可以互相拼接,但必须保证加法运算符 + 的两侧的运算对象至少有一个是string,因为字符串字面值与string是不同的类型。
- 我们经常需要单独处理string对象中的字符,在cctype头文件中定义了一组标准库函数做这件事,比如判断某个字符是否为字母,是否为大写等等。
- C++标准库中除了定义C++语言特有的功能外,也兼容了C语言的标准库。C语言的头文件形如name.h,C++则将这些文件命名为cname,这里的c表示这是一个属于C语言标准库的头文件。特别的,在名为cname的头文件中定义的名字从属于命名空间std,而定义在.h头文件则不然。一般来说,C++程序应该使用名为cname的头文件而不是name.h形式的头文件。
- C++11新标准提供了范围for(range for)语句,可以方便地遍历给定序列中的每个元素
for (auto c : str)
cout<< c <<endl;
- 如果想要改变string对象中字符的值,必须把循环变量定义成引用类型
- 除了范围语句之外,还可以利用下标运算符[]和迭代器访问string对象中的单个字符,下标运算符[ ]接收的输入参数也是string:size_type类型的值。
- 标准库类型vector表示对象的集合,其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引,索引用于访问对象。因为vector“容纳着”其他对象,所以它也被称作容器。要使用vector,必须包含头文件vector。
- vector是一个类模板,由vector生成的类型必须指明vector中元素的类型,例如vector<int>。
- vector能容纳绝大多数类型的对象作为元素,但是因为引用不是对象,所以不能包含引用。
- 定义vector对象的常用方法:
- vector<T> v1 最常见的定义方式
- vector<T> v2(v1)
- vector<T> v2 = v1
- vector<T> v3(n,val) 包含n个val
- vector<T> v4(n) 包含n个执行了值初始化的对象
- vector<T> v5{a,b,c...} v5包含了列表里的元素
- vector<T> v5={a,b,c...}
- 列表初始化是由C++11标准提供的
- 可以只提供vector容纳的对象数量,此时元素初值由元素类型决定,且元素类型必须支持默认初始化。
- 除了所有元素的值都一样的情况之外,定义一个空的vector对象更为高效。
- 如果循环体内部包含有向vector对象添加元素的语言,则不能使用范围for循环。
- 常用vector操作:
- v.empty()
- v.size()
- v.push_back(t)
- v[n]
- v1==v2 当且仅当元素数量相同且对应位置元素值相同
- <,<=,>,>= 以字典顺序进行比较
- 只有当元素的值可比较时,vector对象之间才可比较
- vector的size()返回值是由vector定义的size_type类型。
是vector<int>::size_type而不是vector::size_type,因为前者才是类型。 - vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素。
- 除了使用下标运算符[ ]来访问string和vector的元素,还有一种更通用的机制也可以实现相同的目的,这就是迭代器(iterator)。类似于指针类型,迭代器也提供了对对象的间接访问。
- 和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员。比如,这些类型都拥有名为begin和end的成员,其中begin负责返回指向第一个元素的迭代器,end返回尾元素的下一位置(尾后迭代器,off-the-end iterator)。特殊情况下如果容器为空,则begin和end返回同一个迭代器。
auto b=v.begin(), e = v.end()
- 标准容器迭代器的运算符:
- *iter 返回迭代器iter所指元素的引用
- iter->mem 解引用iter并获取元素名为mem的成员,等价于(*iter).mem
- ++iter/--iter 令iter指示容器的下一个/上一个元素
- iter1==iter2/iter1!=iter2 如果两个迭代器指示同一个元素或同为尾后迭代器,则相等;否则不相等
26.就像不知道string和vector的size_type成员到底是什么类型,一般来说我们也不必知道迭代器的精确类型。而实际上,那些拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器类型:
vector<int>::iterator it; //能读写元素
vector<int>::const_iterator it2; //只读
const_iterator和指向常量的指针差不多,如果vector对象或string对象是一个常量,那么只能用const_iterator。
- begin和end返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_iterator;如果不是常量,返回iterator。但有时候如果只需要进行读操作,我们可以通过C++11标准引入的两个新函数cbegin和cend来获取const_iterator。
- 凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。
- 数组是一种复合类型,数组的声明形如a[d],a是数组的名字,d是元素的个数。编译的时候元素个数应该是已知的,必须是一个常量表达式。
- 和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值。
- 定义数组的时候必须指定数组的类型,不允许用auto关键字。另外和vector一样,数组的元素应为对象,不存在引用的数组。
- 可以对数组元素进行列表初始化,此时允许忽略数组的元素个数。如果指明的元素个数大于列表元素,则剩下的元素被默认初始化。
- 字符数组有一种额外的初始化方式,可以用字符串字面值进行初始化。此时一定要注意到字符串字面值的结尾处还有一个空字符 \0,这个空字符也会被拷贝到字符数组中。
- 不能将数组的内容拷贝给其他数组作为初始值,也不能用数组给其他数组赋值。
- 要想理解数组声明的定义,最好的办法是从数组的名字开始按照由内而外,由右向左的顺序阅读。
int *ptrs[10]; //ptrs是含有10个整数指针的数组
int (*Parray)[10]=&arr; //Parray指向一个含有10个整数的数组
int (&arrRef)[10]=arr; //arrRef引用一个含有10个整数的数组
- 与标准库类型vector和string一样,数组也能使用范围for或运算符来访问。使用数组下标时,通常将其定义为size_t类型。(这是一种机器相关的无符号类型,被设计得足够大以便能表示内存中任意对象的大小)在cstddef头文件中定义。
- 数组有一个特性,很多用到数组名字的地方,编译器都会自动将其替换为一个指向数组首元素的指针。
string nums[]={"one","two"};
string *p=&nums[0]; //p指向nums第一个元素
string *p2=nums; //等价于p2=&nums[0]
必须指出的是,当使用decltype关键字时转换不会发生,返回的类型是一个10个整数构成的数组。
- C++11新标准引入了两个名为begin和end的函数,与容器中的两个同名函数功能类似。
int ia[]={1,2,3,4,5,6};
int *beg=begin(ia);
int *end=end(ia);
while(beg!=end){
cout<<*beg<<endl
++beg;
}
- 当对数组使用下标运算符时,编译器会自动执行转换工作,实际上是对指向元素的指针执行下标运算。只要指针指的是有效范围(元素位置或者尾后位置),都可以执行下标运算。
int *p=&ia[2]; //p指向索引为2的元素
int j=p[1]; //p[1]等价于*(p+1),就是ia[3]表示的元素
itn k=p[-2]; //p[-2]是ia[0]表示的元素
内置的下标运算符索引值不是无符号类型,这点与string和vector不一样。
- 字符串字面值是一种通用结构的实例,这种结构即是C++由C继承而来的C风格字符串。C风格字符串不是一种类型,而是为了表达和使用字符串而形成的一种写法。按此习惯书写的字符串存放在字符数组并以空字符结束('\0')。
- C标准库String函数,它们定义在cstring头文件中:
- strlen(p) 返回p的长度,不包括空字符
- strcmp(p1,p2)
- strcat(p1,p2) 把p2附加到p1之后,返回p1
- strcpy(p1,p2) 把p2拷贝给p1,返回p1
传入此类函数的指针必须指向以空字符作为结束的数组。
- 比较两个C风格字符串的方法和比较标准库string对象的方法大相径庭。比较string时,用的是普通的关系运算符。如果把这些运算符用在C风格字符串上,实际比较的是指针而非字符串本身。
- 使用C标准库函数需要估算数组的大小避免空间不足,但这一点其实很难照顾周全。对大多数应用来说,使用标准库string要比使用C风格字符串更安全,更高效。
- 很多C++程序在标准库之前就已经写好了,它们肯定没用string和vector。另外还存在着很多C++程序是与C语言的接口程序,也无法使用C++标准库。现代C++程序不得不与那些充满了数组和C风格字符串的代码衔接,为了使这一工作简单易行,C++专门提供了一组功能。
- 前文介绍过允许使用字符串字面值来初始化string对象,更一般的情况是,任何出现字符串字面值的地方都可以用以空字符结束的字符数组来替代。但上述性质反过来就不成立,如果某处需要一个C风格字符串,无法直接用string对象代替,为了完成该功能,string专门提供了一个名为c_str的成员函数。
s="hello!";
char *str = s; //错误,不能用string对象初始化char*
const char *str=s.c_str(); //正确
函数的返回结果是一个指针,该指针指向一个以空字符结束的字符数组,这个数组的数据恰好与那个string对象的一样。结果指针的类型是const char*。
我们无法保证c_str函数返回的数组一直有效。事实上,如果后续操作改变了s的值就可能让之前返回的数组失去效用。所以,最好将数组拷贝一份
- 前文说过不允许使用一个数组为另一个内置类型的数组赋初值,也不允许使用vector对象初始化数组。但是,允许使用数组来初始化vector对象,只需指明要拷贝区域的首元素地址和尾后地址就可以了。
int int_arr[]={0,1,2,3,4,5,6};
vector<int> ivec(begin(int_arr),end(int_arr));
- 尽量使用标准库类型而非数组
- 要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。
网友评论