1. 命名空间的 using 声明
使用 using 声明可以在不需要加前缀 namespace_name:: 的情况下访问命名空间中的名字。using 声明的形式如下:
using namespace::name;
一个 using 声明一次只能作用于一个命名空间成员。using 声明可用来明确指定在程序中用到的命名空间中的名字,如果希望使用 std(或其他的命名空间)中的几个名字,则必须为要用到的每个名字都提供一个 using 声明。
有一种情况下,必须总是使用完全限定的标准库名字:在头文件中。理由是头文件的内容会被预处理器复制到程序中。如果在头文件中放置 using 声明,就相当于在包含该头文件 using 的每个程序中都放置了同一 using,不论该程序是否需要 using 声明。
2. 标准库string 类型
用户程序要使用 string 类型对象,必须包含相关头文件。如果提供了合适的 using 声明,那么编写出来的程序将会变得简短些:
#include <string>
using std::string;
因为历史原因以及为了与 C 语言兼容,字符串字面值与标准库 string 类型不是同一种类型。
string s; // empty string
cin >> s; // read whitespace-separated string into s
从标准输入读取 string 并将读入的串存储在 s 中。string 类型的输入操作符:
1)读取并忽略开头所有的空白字符(如空格,换行符,制表符)。
2)读取字符直至再次遇到空白字符,读取终止。
读入未知数目的 string 对象:和内置类型的输入操作一样,string 的输入操作符也会返回所读的数据流。因此,可以把输入操作作为判断条件
string word;
while (cin >> word)
cout << word << endl;
return 0;
使用 getline 读取整行文本:
string line;
while (getline(cin, line))
cout << line << endl;
return 0;
由于 getline 函数返回时丢弃换行符,换行符将不会存储在 string 对象中。由于 line 不含换行符,若要逐行输出需要自行添加。
string 对象的操作
s.empty() 如果 s 为空串,则返回 true,否则返回 false。
s.size() 返回 s 中字符的个数
s[n] 返回 s 中位置为 n 的字符,位置从 0 开始计数
s1 + s2 把 s1 和s2 连接成一个新字符串,返回新生成的字符串
s1 = s2 把 s1 内容替换为 s2 的副本
v1 == v2 比较 v1 与 v2的内容,相等则返回 true,否则返回 false
!=, <, <=, >, and >= 保持这些操作符惯有的含义
string::size_type 类型:事实上,size 操作返回的是 string::size_type 类型的值。string 类类型和许多其他库类型都定义了一些配套类型(companion type)。通过这些配套类型,库类型的使用就能与机器无关(machine-independent)。size_type 就是这些配套类型中的一种。它定义为与 unsigned 型(unsigned int 或 unsigned long)具有相同的含义,而且可以保证足够大能够存储任意 string 对象的长度。为了避免溢出,保存一个 stirng 对象 size 的最安全的方法就是使用标准库类型 string::size_type。
从string对象获取字符:string类型通过下标操作符([ ])来访问 string 对象中的单个字符。下标操作符需要取一个 size_type 类型的值,来标明要访问字符的位置。这个下标中的值通常被称为“下标”或“索引”(index)
用下标操作符分别取出 string 对象的每个字符,分行输出:
string str("some string");
for (string::size_type ix = 0; ix != str.size(); ++ix)
cout << str[ix] << endl;
下标操作可用作左值:str[ix] = '*';
任何可产生整型值的表达式可用作下标操作符的索引。例如,假设 someval 和 someotherval 是两个整形对象,可以这样写:
str[someotherval * someval] = someval;
虽然任何整型数值都可作为索引,但索引的实际数据类型却是类型 unsigned 类型 string::size_type。
应该用 string::size_type 类型的变量接受 size 函数的返回值。在定义用作索引的变量时,出于同样的道理,string 对象的索引变量最好也用 string::size_type 类型。
string 对象中字符的处理
这些函数都在 cctype 头文件中定义。
isalnum(c) 如果 c 是字母或数字,则为 true。
isalpha(c) 如果 c 是字母,则为 true。
iscntrl(c) 如果 c 是控制字符,则为 true
isdigit(c) 如果 c 是数字,则为 true。
isgraph(c) 如果 c 不是空格,但可打印,则为 true。
islower(c) 如果 c 是小写字母,则为 true。
isprint(c) 如果 c 是可打印的字符,则为 true。
ispunct(c) 如果 c 是标点符号,则 true。
isspace(c) 如果 c 是空白字符,则为 true。
isupper(c) 如果 c 是大写字母,则 true。
isxdigit(c) 如果是 c 十六进制数,则为 true。
tolower(c) 如果 c 大写字母,返回其小写字母形式,否则直接返回 c。
toupper(c) 如果 c 是小写字母,则返回其大写字母形式,否则直接返回 c。
可打印的字符是指那些可以表示的字符,空白字符则是空格、制表符、垂直制表符、回车符、换行符和进纸符中的任意一种;标点符号则是除了数字、字母或(可打印的)空白字符(如空格)以外的其他可打印字符。
C++ 标准库除了定义了一些选定于 C++ 的设施外,还包括 C 标准库。C 标准库头文件命名形式为 name 而 C++ 版本则命名为 cname ,少了后缀,.h 而在头文件名前加了 c 表示这个头文件源自 C 标准库。因此,cctype 与 ctype.h 文件的内容是一样的,只是采用了更适合 C++程序的形式。特别地,cname 头文件中定义的名字都定义在命名空间 std 内,而 .h 版本中的名字却不是这样。通常,C++ 程序中应采用 cname 这种头文件的版本,而不采用 name.h 版本,这样,标准库中的名字在命名空间 std 中保持一致。
3. 标准库 vector 类型
vector 是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。和 string 对象一样,标准库将负责管理与存储元素相关的内存。
使用 vector 之前,必须包含相应的头文件。本书给出的例子,都是假设已作了相应的 using 声明:
#include <vector>
using std::vector;
vector 是一个类模板(class template)。使用模板可以编写一个类定义或函数定义,而用于多个不同的数据类型。
vector<int> ivec; // ivec holds objects of type int
vector<Sales_item> Sales_vec; // holds Sales_items
vector 不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型。vector 类型的每一种都指定了其保存元素的类型。因此,vector<int> 和 vector<string> 都是数据类型。
vector 对象的构造函数:
vector<T> v1; 默认构造函数 v1 为空。
vector<T> v2(v1); v2 是 v1 的一个副本。v1和v2必须同类型
vector<T> v3(n, i); v3 包含 n 个值为 i 的元素。
vector<T> v4(n); v4 has n copies of a value-initialized object
vector对象动态增长:vector 对象(以及其他标准库容器对象)的重要属性就在于可以在运行时高效地添加元素。因为 vector 增长的效率高,在元素值已知的情况下,最好是动态地添加元素。
值初始化:如果没有指定元素的初始化式,那么标准库将自行提供一个元素初始值进行值初始化(value initializationd)。这个由库生成的初始值将用来初始化容器中的每个元素,具体值为何,取决于存储在 vector 中元素的数据类型。
vector<string> fvec(10); // 10 elements, each initialized to 0
vector<string> svec(10); // 10 elements, each an empty string
对于有自定义构造函数但没有默认构造函数的类,在初始化这种类型的 vector 对象时,程序员就不能仅提供元素个数,还需要提供元素初始值。
如果元素类型是没有定义任何构造函数的类类型。这种情况下,标准库仍产生一个带初始值的对象,这个对象的每个成员进行了值初始化。
vector 对象的操作:
v.empty() 如果 v 为空,则返回 true,否则返回 false。
v.size() 返回 v 中元素的个数。(返回相应 vector 类定义的 size_type 的值)
v.push_back(t) 在 v 的末尾增加一个值为 t 的元素。
v[n] 返回 v 中位置为 n 的元素。
v1 = v2 把 v1 的元素替换为 v2 中元素的副本。
v1 == v2 如果 v1 与 v2 相等,则返回 true。
!=, <, <=, >, and >= 保持这些操作符惯有的含义。
使用 size_type 类型时,必须指出该类型是在哪里定义的。vector 类型总是包括总是包括 vector 的元素类型:
vector<int>::size_type // ok
vector::size_type // error
向 vector 添加元素:
string word;
vector<string> text; // empty vector
while (cin >> word) {
text.push_back(word); // append word to text
}
vector 的下标操作:vector 中的对象是没有命名的,可以按 vector 中对象的位置来访问它们。通常使用下标操作符来获取元素:
// reset the elements in the vector to zero
for (vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
ivec[ix] = 0;
下标操作不添加元素:
vector<int> ivec; // empty vector
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec[ix] = ix; // disaster: ivec has no elements
仅能对确知已存在的元素进行下标操作!
4. 迭代器简介
标准库为每一种标准容器(包括 vector)定义了一种迭代器类型。迭代器类型提供了比下标操作更通用化的方法:所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。因为迭代器对所有的容器都适用,现代 C++ 程序更倾向于使用迭代器而不是下标操作访问容器元素,即使对支持下标操作的 vector 类型也是这样。
每种容器类型都定义了自己的迭代器类型,如 vector:
vector<int>::iterator iter;
定义了一个名为 iter 的变量,它的数据类型是 vector<int> 定义的 iterator 类型。每个标准库容器类型都定义了一个名为 iterator 的成员,这里的 iterator 与迭代器实际类型的含义相同。
迭代器和迭代器类型:同一个术语 iterator 往往表示两个不同的事物。一般意义上指的是迭代器的概念;而具体而言时指的则是由容器定义的具体的 iterator 类型,如 vector<int>。重点要理解的是,有许多用作迭代器的类型,这些类型在概念上是相关的。若一种类型支持一组确定的操作(这些操作可用来遍历容器内的元素,并访问这些元素的值),我们就称这种类型为迭代器。各容器类都定义了自己的 iterator 类型,用于访问容器内的元素。换句话说,每个容器都定义了一个名为 iterator 的类型,而这种类型支持(概念上的)迭代器的各种操作。
begin和end 操作:每种容器都定义了一对命名为 begin 和 end 的函数,用于返回迭代器。如果容器中有元素的话,由 begin 返回的迭代器指向第一个元素:
vector<int>::iterator iter = ivec.begin();
由 end 操作返回的迭代器指向 vector 的“末端元素的下一个”。“超出末端迭代器”(off-the-end iterator)。表明它指向了一个不存在的元素。如果 vector 为空,begin 返回的迭代器与 end 返回的迭代器相同。
vector 迭代器的自增和解引用运算:迭代器类型定义了一些操作来获取迭代器所指向的元素,并允许程序员将迭代器从一个元素移动到另一个元素。
迭代器类型可使用解引用操作符(dereference operator)(*)来访问迭代器所指向的元素:*iter = 0;
迭代器使用自增操作符向前移动迭代器指向容器中下一个元素。
可以用 == 或 != 操作符来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。
const_iterator:每种容器类型还定义了一种名为 const_iterator 的类型,该类型只能用于读取容器内元素,但不能改变其值。当我们对普通 iterator 类型解引用时,得到对某个元素的非 const。而如果我们对 const_iterator 类型解引用时,则可以得到一个指向 const 对象的引用,如同任何常量一样,该对象不能进行重写。
使用 const_iterator 类型时,我们可以得到一个迭代器,它自身的值可以改变,但不能用来改变其所指向的元素的值。可以对迭代器进行自增以及使用解引用操作符来读取值,但不能对该元素赋值。
const_iterator 对象与 const 的 iterator 对象的区别:声明一个 const 迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的值:
vector<int> nums(10); // nums is nonconst
const vector<int>::iterator cit = nums.begin();
*cit = 1; // ok: cit can change its underlying element
++cit; // error: can't change the value of cit
迭代器的算术操作:除了一次移动迭代器的一个元素的增量操作符外,vector 迭代器(其他标准库容器迭代器很少)也支持其他的算术操作。这些操作称为迭代器算术操作(iterator arithmetic),包括:
iter + n
iter - n
iter1 - iter2:该表达式用来计算两个迭代器对象的距离,该距离是名为 difference_type 的 signed 类型 size_type 的值
可以用迭代器算术操作来移动迭代器直接指向某个元素,例如,下面语句直接定位于 vector 中间元素: vector<int>::iterator mid = vi.begin() + vi.size() / 2;
任何改变 vector 长度的操作都会使已存在的迭代器失效。例如,在调用 push_back 之后,就不能再信赖指向 vector 的迭代器的值了
5. 标准库 bitset