通常,头文件中应该只定义确定必要的东西。请养成这个习惯。
标准库string类型
几种初始化string对象的方式
string s2(s1); // 将s2初始化为s1的一个副本
string s3( " value " ); // 将s3初始化为一个字符串字面值副本
string s4(n, ' c ' ); // 将s4初始化为字符'c'的n个副本
字符串字面值与标准库string类型不是同一种类型。
由于getline函数返回时丢弃换行符,换行符将不会存储在string对象中。
string对象的操作
s.size() // 返回s中字符的个数
s[n] // 返回s中位置为n的字符,位置从0开始计数
...
string::size_type类型
它定义为与unsigned型具有相同的含义,而且可以保证足够大能够存储任意string对象的长度。
任何存储string的size操作结果的变量必须为string::size_type类型。特别重要的是,不要把size的返回值赋给一个int变量。
和字符串字面值的连接
当进行string对象和字符串字面值混合连接操作时,+操作符的左右操作数必须至少有一个是string类型的:
string s2 = " world " ;
string s3 = s1 + " , " ; // ok: adding a string and a literal
string s4 = " hello " + " , " ; // error: no string operand
string s5 = s1 + " , " + " world " ; // ok: each + has string operand
string s6 = " hello " + " , " + s2; // error: can't add string literals
计算下标值
任何可产生整型值的表达式都可用作下标操作符的索引。例如,假设someval和someotherval是两个整型对象,可以这样写:
在定义用作索引的变量时,string对象的索引变量最好也用string::size_type。
string对象中字符的处理
cctype定义的函数
isgraph(c) islower(c) isprint(c) ispunct(c)
isspace(c) isupper(c) isxdigit(c)
tolower(c) toupper(c)
建议:采用C标准库头文件的C++版本
C标准库头文件命名形式为name.h,而C++版本则命名为cname,cname与name.h文件的内容是一样的,只是采用了更适合C++程序的形式。特别地,cname头文件中定义的名字都定义在命名空间std内,而.h版本中的名字却不是这样。
通常,C++程序中应采用cname这种头文件的版本,而不采用name.h版本,这样,标准库中的名字在命名空间std中保持一致。使用.h版本会给程序员带来负担,因为他们必须记得哪些标准库名字是从C继承来的,哪些是C++所特有的。
标准库vector类型
vector不是一种数据类型,而只是一个类模版,可以用来定义任意多种数据类型。vector类型的每一种都指定了其保存元素的类型。因此,vector<int>和vector<string>都是数据类型。
vector对象的定义和初始化
几种初始化vector对象的方式
vector < T > v2(v1); // v2是v1的一个副本
vector < T > v3(n, i); // v3包含n个值为i的元素
vector < T > v4(n); // v4含有值初始化的元素的n个副本
虽然可以对给定元素个数的vector对象预先分配内存,但更有效的方法是先初始化一个空vector对象,然后再动态地增加元素。
vector对象的操作
vector操作
vector的下标操作
下例使用for循环把vector中的每个元素值都重置为0:
for (vector < int > ::size_type ix = 0 ; ix != ivec.size(); ++ ix)
ivec[ix] = 0 ;
关键概念:安全的泛型编程
C++程序员习惯于优先选用!=而不是<来编写循环判断条件。调用size成员函数而不保存它的返回值,反映了一种良好的编程习惯。在C++中,有的数据结构(如vector)可以动态增长。上例中循环仅需要读取元素,而不需要增加新的元素。但是,循环可以容易地增加新元素,如果确实增加了新元素的话,那么测试已保存的size值作为循环的结束条件就会有问题,因为没有将新加入的元素计算在内。所以我们倾向于在每次循环中测试size的当前值,而不是在进入循环前,存储size值的副本。
下标操作不添加元素
初学C++的程序员可能会认为vector的下标操作可以添加元素,其实不然:
for (vector < int > ::size_type ix = 0 ; ix != 10 ; ++ ix)
ivec[ix] = ix; // disaster: ivec has no elements
这个循环的正确写法应该是:
ivec.push_back(ix); // ok: adds new element with value ix
必须是已存在的元素才能用下标操作符进行索引。通过下标操作进行赋值时,不会添加任何元素。
警告:仅能对确知已存在的元素进行下标操作
对于下标操作符([]操作符)的使用有一点非常重要,就是仅能提取确实已存在的元素,例如:
cout << ivec[0]; // Error: ivec has no elements!
vector<int> ivec2(10); // vector with 10 elements
cout << ivec[10]; // Error: ivec has elements 0...9
迭代器简介
vector迭代器的自增和解引用运算
迭代器类型可使用解引用操作符(*操作符)来访问迭代器所指向的元素:
解引用操作符返回迭代器当前所指向的元素。假设iter指向vector对象ivec的第一个元素,那么*iter和ivec[0]就是指向同一个元素。
由于end操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。
const_iterator
该类型只能用于读取容器内元素,但不能改变其值。
如果我们对const_iterator类型解引用时,可以得到一个指向const对象的引用,如同任何常量一样,该对象不能进行重写。
例:
for (vector < string > ::const_iterator iter = text.begin();
iter != text.end(); ++ iter)
cout << * iter << endl; // print each element in text
for (vector < string > ::const_iterator iter = text.begin();
iter != text.end(); ++ iter)
* iter = " " ; // error: *iter is const
不要把const_iterator对象与const的iterator对象混淆起来。声明一个const迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的值:
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
const_iterator对象可以用于const vector或非const vector,因为不能改写元素值。const迭代器这种类型几乎没什么用处:一旦它被初始化后,只能用它来改写其指向的元素。
vector < int > ::const_iterator
// an iterator whose value cannot change
const vector < int > ::iterator
迭代器的算术操作
iter1 - iter2 // 该表达式用来计算两个迭代器对象的距离。
// 该距离是名为difference_type的signed类型的值
vector < int > ::iterator mid = vi.begin() + vi.size() / 2 ;
标准库bitset类型
类似于vector,bitset类是一种类模版;而与vector不一样的是bitset类型对象的区别仅在其长度而不在其类型。在定义bitset时,要明确bitset含有多少位,须在尖括号内给出它的长度值:
给出的长度值必须是常量表达式。
初始化bitset对象的方法
bitset < n > b(u); // b是unsigned long型u的一个副本
bitset < n > b(s);
bitset < n > b(s, pos, n); // b是s中从位置pos开始的n个位的副本
用unsigned值初始化bitset对象
当用unsigned long值作为bitset对象的初始值时,该值将转化为二进制的位模式。
bitset < 32 > bitvec2( 0xffff ); // bits 0...15 are set to 1; 16...31 are 0
// on a 32-bit machine, bits 0 to 31 initialized from 0xffff
bitset < 128 > bitvec3( 0xffff ); // bits 32 through 127 initialized to zero
用string对象初始化bitset对象
当用string对象初始化bitset对象时,string对象直接表示为位模式。从string对象读入位集的顺序是从右向左。
string对象和bitset对象之间是反向转化的:string对象的最右边字符(即下标最大的那个字符)用来初始化bitset对象的低阶位(即下标为0的位)。当用string对象初始化bitset对象时,记住这一差别很重要。
bitset对象上的操作
b.test(pos) b. set () b. set (pos) b.reset() b.reset(pos)
b.flip() b.flip(pos) b.to_ulong() os << b