string 类型支持长度可变的字符串,C++ 标准库将负责管理与存储字符相关的内存,以及提供各种有用的操作。
#include <string>
using std::string;
字符串字面值与标准库 string 类型不是同一种类型。
用 iostream 和 string 标准库,使用标准输入输出操作符来读写 string 对象
读取并忽略开头所有的空白字符(如空格,换行符,制表符)。
读取字符直至再次遇到空白字符,读取终止。
读入未知数目的 string 对象
使用 getline 读取整行文本
getline 函数从输入流的下一行读取,并保存读取的内容到不包括换行符。和输入操作符不一样的是,getline 并不忽略行开头的换行符。只要 getline 遇到换行符,即便它是输入的第一个字符,getline 也将停止读入并返回。如果第一个字符就是换行符,则 string 参数将被置为空 string。由于 line 不含换行符,若要逐行输出需要自行添加。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string word;
// read until end-of-file, writing each word to a new line
while (cin >> word)
{
cout << word << endl;
}
string line;
// read line at time until end-of-file
while (getline(cin, line))
{
cout << line << endl;
}
return 0;
}
关于size_type:
string 类类型和许多其他库类型都定义了一些配套类型(companion type)。通过这些配套类型,库类型的使用就能与机器无关(machine-independent)。size_type 就是这些配套类型中的一种。它定义为与 unsigned 型(unsigned int 或 unsigned long)具有相同的含义,而且可以保证足够大能够存储任意 string 对象的长度。
string 关系操作符
关系操作符比较两个 string 对象时采用了和(大小写敏感的)字典排序相同的策略
如果两个 string 对象长度不同,且短的 string 对象与长的 string 对象的前面部分相匹配,则短的 string 对象小于长的 string 对象。
如果 string 对象的字符不同,则比较第一个不匹配的字符
两个 string 对象相加
string 对象的加法被定义为连接(concatenation)。也就是说,两个(或多个)string 对象可以通过使用加操作符 + 或者复合赋值操作符 +=连接起来。
string和字符串字面值的连接
当进行 string 对象和字符串字面值混合连接操作时,+ 操作符的左右操作数必须至少有一个是 string 类型的
从 string 对象获取字符
string 类型通过下标操作符([ ])来访问 string 对象中的单个字符。下标操作符需要取一个 size_type 类型的值,来标明要访问字符的位置。
string 对象的下标从 0 开始。如果 s 是一个 string 对象且 s 不空,则 s[0] 就是字符串的第一个字符, s[1] 就表示第二个字符(如果有的话),而 s[s.size() - 1] 则表示 s 的最后一个字符。
引用下标时如果超出下标作用范围就会引起溢出错误。
下标操作可用作左值
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1 = "hello"; // no punctuation
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
for (string::size_type ix = 0; ix != s5.size(); ++ix)
{
cout << s5[ix];
}
cout << endl;
for (string::size_type ix = 0; ix != s5.size(); ++ix)
{
s5[ix] = '*';
}
return 0;
}
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
string line;
getline(cin,line);
int alpha = 0;
int digit = 0;
int punct = 0;
int space = 0;
for(string::size_type index=0; index != line.size(); ++index)
{
if(isalpha(line[index]))
{
++alpha;
}
if(isdigit(line[index]))
{
++digit;
}
if(ispunct(line[index]))
{
++punct;
}
if(isspace(line[index]))
{
++space;
}
}
cout << "alpha : " << alpha << endl;
cout << "digit : " << digit << endl;
cout << "punct : " << punct << endl;
cout << "space : " << space << endl;
return 0;
}
vector 是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。和 string 对象一样,标准库将负责管理与存储元素相关的内存。
一个容器中的所有对象都必须是同一种类型的。
#include <vector>
using std::vector;
vector 不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型。vector 类型的每一种都指定了其保存元素的类型。因此,vector<int> 和 vector<string> 都是数据类型。
虽然可以对给定元素个数的 vector 对象预先分配内存,但更有效的方法是先初始化一个空 vector 对象,然后再动态地增加元素.
如果没有指定元素的初始化式,那么标准库将自行提供一个元素初始值进行值初始化,具体值为何,取决于存储在 vector 中元素的数据类型
使用 size_type 类型时,必须指出该类型是在哪里定义的。vector 类型总是包括 vector 的元素类型:
向 vector 添加元素
vector 的下标操作类似于 string 类型的下标操作,vector 下标操作的结果为左值
下标操作不添加元素
下标只能用于获取已存在的元素。
必须是已存在的元素才能用下标操作符进行索引。通过下标操作进行赋值时,不会添加任何元素。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> ivec; // empty vector
// vector::size_type index; error!
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
{
// ivec[ix] = ix; disaster: ivec has no elements
ivec.push_back(ix); //ok
}
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
{
cout << ivec[ix] << endl;
}
system("pause");
return 0;
}
迭代器
迭代器(iterator)迭代器是一种检查容器内元素并遍历元素的数据类型。
所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作
每种容器类型都定义了自己的迭代器类型,如 vector:
vector<int>::iterator iter;
如果容器中有元素的话,由 begin 返回的迭代器指向第一个元素
由 end 操作返回的迭代器指向 vector 的“末端元素的下一个”。
如果 vector 为空,begin 返回的迭代器与 end 返回的迭代器相同
由 end 操作返回的迭代器并不指向 vector 中任何实际的元素,相反,它只是起一个哨兵(sentinel)的作用,表示我们已处理完 vector 中所有元素。
代器类型可使用解引用操作符(dereference operator)(*)来访问迭代器所指向的元素
代器类支持自增和自减操作
由于 end 操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。(指向不确定数据)
用 == 或 != 操作符来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。
每种容器类型还定义了一种名为 const_iterator 的类型,该类型只能用于读取容器内元素,但不能改变其值。
它自身的值可以改变,但不能用来改变其所指向的元素的值。
不要把 const_iterator 对象与 const 的 iterator 对象混淆起来。声明一个 const 迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的值:
迭代器的算术操作
iter + n
iter - n
其位置在 iter 所指元素之前(加)或之后(减) n 个元素的位置。
iter1 - iter2
该表达式用来计算两个迭代器对象的距离,该距离是名为 difference_type 的 signed 类型 size_type 的值
iter1 与 iter2 两者必须都指向同一 vector 中的元素,或者指向 vector 末端之后的下一个元素。
下面语句直接定位于 vector 中间元素:
任何改变 vector 长度的操作都会使已存在的迭代器失效。例如,在调用 push_back 之后,就不能再信赖指向 vector 的迭代器的值了
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> ivec1;
for(int i=0; i<10; ++i)
{
ivec1.push_back(i+10);
}
for(vector<int>::iterator iter=ivec1.begin(); iter!=ivec1.end(); ++iter)
{
cout << *iter << " ";
}
cout << endl;
vector<int> ivec2(10,9);
cout << "ivec1 == ivec2 ? " << boolalpha << (ivec1 == ivec2) << endl;
vector<int>::iterator mid = ivec1.begin() + ivec1.size() / 2;
cout << "*(mid-2) = " << *(mid-2) << endl;
cout << "*(mid+2) = " << *(mid+2) << endl;
vector<int>::iterator iterBeg = ivec1.begin();
cout << "*iterBeg = " << *iterBeg << endl; //print the first number
vector<int>::iterator iterEnd = ivec1.end();
cout << "*iterEnd = " << *iterEnd << endl; //will print a random number
cout << "iterEnd - iterBeg = " << (iterEnd - iterBeg) << endl;
for (vector<int>::const_iterator iter = ivec1.begin(); iter != ivec1.end(); ++ iter)
{
// *iter = 1; error: *iter is const
cout << *iter << " "; //readonly
}
cout << endl;
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<int> vi;
vi.push_back(4);
vi.push_back(5);
vi.push_back(6);
vi.push_back(7);
vector<int>::iterator iter = vi.begin();
cout << *(iter+1) << endl;
cout << *(iter+2) << endl;
cout << *(iter+3) << endl;
vi.push_back(8);
cout << *(iter+1) << endl;
cout << *(iter+2) << endl;
cout << *(iter+3) << endl;
cout << *(iter+4) << endl; //will print 8?
return 0;
}
标准库提供的 bitset 类简化了位集的处理
#include <bitset>
using std::bitset;
按位置来访问,以 0 位开始的位串是低阶位(low-order),以 31 位结束的位串是高阶位(high-order)。
当用 unsigned long 值作为 bitset 对象的初始值时,该值将转化为二进制的位模式。
当用 string 对象初始化 bitset 对象时,string 对象直接表示为位模式。
size_t 类型定义在 cstddef 头文件中,该文件是 C 标准库的头文件 stddef.h 的 C++ 版本。它是一个与机器相关的 unsigned 类型,其大小足以保证存储内在中对象的大小。
可以用下标操作符来读或写某个索引位置的二进制位,
除了用下标操作符,还可以用 set;、test 和 reset 操作来测试或设置给定二进制位的值
#include <iostream>
#include <string>
#include <bitset>
using namespace std;
int main()
{
bitset<8> bs;
cout << "bitset<8> : " << bs << endl;
// bitvec1 is smaller than the initializer
// bits 0 ... 15 are set to 1
bitset<16> bitvec1(0xffff);
cout << "bitvec1 : " << bitvec1 << endl;
// bitvec2 same size as initializer
// bits 0 ... 15 are set to 1; 16 ... 31 are 0
bitset<32> bitvec2(0xffff);
cout << "bitvec2 : " << bitvec2 << endl;
// on a 32-bit machine, bits 0 to 31 initialized from 0xffff
// bits 32 through 127 initialized to zero
bitset<64> bitvec3(0xffff);
cout << "bitvec3 : " << bitvec3 << endl;
unsigned long ulong = bitvec3.to_ulong();
cout << "bitvec3.to_ulong() = " << ulong << endl;
string strval("1100");
bitset<32> bitvec4(strval);
cout << "bitvec4 : " << bitvec4 << endl;
string str("1111111000000011001101");
bitset<32> bitvec5(str, 5, 4); // 4 bits starting at str[5], 1100
cout << "bitvec5 : " << bitvec5 << endl;
bitset<32> bitvec6(str, str.size() - 4); // use last 4 characters
cout << "bitvec6 : " << bitvec6 << endl;
bitset<32> bitvec7; // 32 bits, all zero
bool is_set = bitvec7.any(); // false, all bits are zero
cout << "is_set : " << boolalpha << is_set << endl;
bool is_not_set = bitvec7.none(); // true, all bits are zero
cout << "is_not_set : " << boolalpha << is_not_set << endl;
size_t bits_set = bitvec7.count(); // returns number of bits that are on
cout << "bits_set : " << bits_set << endl;
bitset<8> bitvec(strval); // 00001100
cout << boolalpha << "bitvec.test(0) : " << bitvec.test(0) << endl;
cout << boolalpha << "bitvec.test(2) : " << bitvec.test(2) << endl;
bitvec.set(6); // set all the bits to 0.
cout << "after bitvec.set(6) : " << bitvec << endl;
bitvec.reset(3); // set all the bits to 0.
cout << "after bitvec.reset(3) : " << bitvec << endl;
bitvec.reset(); // set all the bits to 0.
cout << "after reset : " << bitvec << endl;
bitvec.set(); // set all the bits to 1
cout << "after set : " << bitvec << endl;
bitvec.flip(0); // reverses value of first bit
cout << "after bitvec.flip(0) : " << bitvec << endl;
bitvec[0].flip(); // also reverses the first bit
cout << "after bitvec[0].flip() : " << bitvec << endl;
bitvec.flip(); // reverses value of all bits
cout << "after bitvec.flip() : " << bitvec << endl;
return 0;
}