C++primer第三章标准库类型
除第二章介绍的基本数据类型外,C++ 还定义了一个内容丰富的抽象数据类型标准库。
本章将介绍标准库中的 vector、string 和 bitset 类型。
string 类型支持长度可变的字符串
vector 可用于保存一组指定类型的对象
bitset,提供了一种抽象方法来操作位的集合。与整型值上的内置位操作符相比,bitset 类类型提供了一种更方便的处理位的方式。
第二章所涉及的类型都是低层数据类型:这些类型表示数值或字符的抽象,并根据其具体机器表示来定义。
除了这些在语言中定义的类型外,C++ 标准库还定义了许多更高级的抽象数据类型之所以说这些标准库类型是更高级的,是因为其中反映了更复杂的概念;之所以说它们是抽象的,是因为我们在使用时不需要关心它们是如何表示的,只需知道这些抽象数据类型支持哪些操作就可以了。
在继续探究标准库类型之前,我们先看一种机制,这种机制能够简化对标准库中所定义名字的访问。
3.1. 命名空间的 using 声明
std::cin
:: 操作符,该操作符是作用域操作符
作用域:右操作数的名字可以在左操作数的作用域中找到。
C++ 提供了更简洁的方式来使用命名空间成员:using 声明
using namespace::name;
1 #include <string> 2 #include <iostream> 3 // using declarations states our intent to use these names from the 4 namespace std 5 using std::cin; 6 using std::string; 7 int main() 8 { 9 string s; // ok: string is now a synonym for std::string 10 cin >> s; // ok: cin is now a synonym for std::cin 11 cout << s; // error: no using declaration; we must use full 12 name 13 std::cout << s; // ok: explicitly use cout from namepsace std 14 }
每个名字都需要一个 using 声明
using 声明可用来明确指定在程序中用到的命名空间中的名字,如果希望使用 std(或其他的命名空间)中的几个名字,则必须为要用到的每个名字都提供一个 using 声明。
1 #include <iostream> 2 // using declarations for names from the standard library 3 using std::cin; 4 using std::cout; 5 using std::endl; 6 int main() 7 { 8 cout << "Enter two numbers:" << endl; 9 int v1, v2; 10 cin >> v1 >> v2; 11 cout << "The sum of " << v1 12 << " and " << v2 13 << " is " << v1 + v2 << endl; 14 return 0; 15 }
有一种情况下,必须总是使用完全限定的标准库名字:在头文件中。理由是头文件的内容会被预处理器复制到程序中。
3.2. 标准库 string 类型
string 类型支持长度可变的字符串,C++ 标准库将负责管理与存储字符相关的内存,以及提供各种有用的操作。
#include <string>
using std::string;
3.2.1. string 对象的定义和初始化
表 3.1 列出了几个 string 类型常用的构造函数。
3.2.2. string 对象的读写
第一章学习了用 iostream 标准库来读写内置类型的值
// Note: #include and using declarations must be added to compile this code int main() { string s; // empty string cin >> s; // read whitespace-separated string into s cout << s << endl; // write s to the output return 0; }
读入未知数目的string 对象
和内置类型的输入操作一样,string 的输入操作符也会返回所读的数据流。因此,可以把输入操作作为判断条件。
int main() { string word; // read until end-of-file, writing each word to a new line while (cin >> word) cout << word << endl; return 0; }
使用getline 读取整行文本
另外还有一个有用的 string IO 操作:getline。这个函数接受两个参数:一个输入流对象和一个 string 对象。
getline 函数从输入流的下一行读取,并保存读取的内容到不包括换行符。和输入操作符不一样的是,getline 并不忽略行开头的换行符。只要 getline 遇到换行符,即便它是输入的第一个字符,getline 也将停止读入并返回。
int main() { string line; // read line at time until end-of-file while (getline(cin, line)) cout << line << endl; return 0; }
3.2.3. string 对象的操作
string 的 size 和 empty 操作
int main() { string st("The expense of spirit\n"); cout << "The size of " << st << "is " << st.size() << " characters, including the newline" << endl; return 0; }
string::size_type 类型
size 操作返回的是 string::size_type 类型的值。
string 类类型和许多其他库类型都定义了一些配套类型(companion type)。
size_type 就是这些配套类型中的一种。它定义为与 unsigned 型(unsignedint 或 unsigned long)具有相同的含义,而且可以保证足够大能够存储任意 string 对象的长度。为了使用由 string 类型定义的 size_type 类型是由 string 类定义。
string 关系操作符
关系操作符比较两个 string 对象时采用了和(大小写敏感的)字典排序相同的策略:
• 如果两个 string 对象长度不同,且短的 string 对象与长的 string 对象的前面部分相匹配,则短的 string 对象小于长的 string 对象。
• 如果 string 对象的字符不同,则比较第一个不匹配的字符。string
string substr = "Hello"; string phrase = "Hello World"; string slang = "Hiya";
string 对象的赋值
// st1 is an empty string, st2 is a copy of the literal string st1, st2 = "The expense of spirit"; st1 = st2; // replace st1 by a copy of st2
两个string 对象相加
string s1("hello, ");
string s2("world\n");
下面把两个 string 对象连接起来产生第三个 string 对象:
string s3 = s1 + s2; // s3 is hello, world\n
和字符串字面值的连接
当进行 string 对象和字符串字面值混合连接操作时,+ 操作符的左右操作数必须至少有一个是 string 类型的:
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
从string 对象获取字符
string str("some string"); for (string::size_type ix = 0; ix != str.size(); ++ix) cout << str[ix] << endl;
下标操作可用作左值
for (string::size_type ix = 0; ix != str.size(); ++ix) str[ix] = '*';
计算下标值
str[someotherval * someval] = someval;
3.2.4. string 对象中字符的处理
string s("Hello World!!!"); string::size_type punct_cnt = 0; // count number of punctuation characters in s for (string::size_type index = 0; index != s.size(); ++index) if (ispunct(s[index])) ++punct_cnt; cout << punct_cnt << " punctuation characters in " << s << endl;
3.3. 标准库 vector 类型
vector 是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。和 string 对象一样,标准库将负责管理与存储元素相关的内存。我们把 vector称为容器,是因为它可以包含其他对象。一个容器中的所有对象都必须是同一种类型的。
使用 vector 之前,必须包含相应的头文件。本书给出的例子,都是假设已作了相应的 using 声明:
#include <vector>
using std::vector;
3.3.1. vector 对象的定义和初始化
值初始化
如果没有指定元素的初始化式,那么标准库将自行提供一个元素初始值进行值初始化(value initializationd)。
3.3.2. vector 对象的操作
3.4. 迭代器简介
迭代器是一种检查容器内元素并遍历元素的数据类型。
容器的iterator 类型
每种容器类型都定义了自己的迭代器类型,如 vector:
vector<int>::iterator iter;
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;
迭代器使用自增操作符(1.4.1 节)向前移动迭代器指向容器中下一个元素。
迭代器的其他操作
另一对可执行于迭代器的操作就是比较:用 == 或 != 操作符来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。
3.5. 标准库 bitset
#include <bitset>
using std::bitset;
3.5.1. bitset 对象的定义和初始化