📄 c++primer
字号:
v.empty() 如果 v 为空,则返回 true,否则返回 false。 v.size() 返回 v 中元素的个数。 v.push_back(t) 在 v 的末尾增加一个值为 t 的元素。 v[n] 返回 v 中位置为 n 的元素。 v1 = v2 把 v1 的元素替换为 v2 中元素的副本。 v1 == v2 如果 v1 与 v2 相等,则返回 true。 !=, <, <=, >, and >= 保持这些操作符惯有的含义。 (1) vector 对象的 size empty() 和 size() 操作类似于 string 的相关操作。 vector 类型总是包括总是包括 vector 的元素类型: vector<int>::size_type // ok vector::size_type // error(3) 向 vector 添加元素 // 从标准输入读单词,添加到 text 中 string word; vector<string> text; // 空的 vector while (cin >> word) { text.push_back(word); // 添加 word 到 text 后面 }(4) vector 的下标操作 // 使用 for 循环把 vector 中的每个元素值都重置为 0: for (vector<int>::size_type ix = 0; ix != ivec.size(); ++ix) ivec[ix] = 0; C++ 中有些函数可以声明为内联(inline)函数。编译器会将内联函数直接替换成相应代码,无需函数调用。 像 size() 这样的小库函数一般定义为内联函数,所以每次循环过程中调用它的运行时代价是比较小的。(5) 下标操作不添加元素 vector<int> ivec; // 空的 vector // ivec 中插入 10 个新元素,错误的写法: for (vector<int>::size_type ix = 0; ix != 10; ++ix) ivec[ix] = ix; // 灾难性错误: 操作 ivec 不存在的元素,越界出错! // ivec 中插入 10 个新元素,应该这么写: for (vector<int>::size_type ix = 0; ix != 10; ++ix) ivec.push_back(ix); // 添加新元素 C++ 程序员习惯于优先选用 != 而不是 < 来编写循环判断条件。 习惯于 C 或 Java 编程的程序员可能会觉得难以理解,学习第二部分的泛型编程后,就会明白这种习惯的合理性。 警告:任何下标操作只能对确知已存在的元素进行! “缓冲区溢出”错误就是对不存在的元素进行下标操作所造致,这样的缺陷是最常见的安全问题。3.4. 迭代器简介 标准库还提供了另一种访问元素的方法:使用迭代器(iterator)。 迭代器是一种检查容器内元素并遍历元素的数据类型。 所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。 例如,string、vector、bitset、list、map 都支持迭代器;string、vector、bitset 支持下标操作。 因为迭代器对所有的容器都适用,现代 C++ 程序更倾向于使用迭代器, 而不是下标操作访问容器元素,即使对支持下标操作的 vector 类型也是这样。 第11章将详细讨论迭代器的工作原理,但使用迭代器并不需要完全了解它复杂的实现细节。 // vector 对象使用迭代器 #include <iostream> #include <vector> // 为了使用 vector 类模板 //using std::vector; //using std::cout; //using std::endl; using namespace std; int main() { // 定义一个vector vector<int> ivec; // 添加10元素 for (vector<int>::size_type ix = 0; ix != 10; ++ix) ivec.push_back(ix); // 添加新元素 // 用下标遍历 for (vector<int>::size_type ix = 0; ix != ivec.size(); ++ix) cout << ivec[ix] << " ";// 0 1 2 3 4 5 6 7 8 9 cout << endl; // 用迭代器遍历 for (vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter) cout << *iter << " "; // 0 1 2 3 4 5 6 7 8 9 cout << endl; // 迭代器的算术操作 vector<int>::iterator mid; mid = ivec.begin() + ivec.size() / 2; // mid 指向中间元素 cout << *mid << endl; // 5 cout << *(mid - 2) << endl; // 3 cout << *(mid + 2) << endl; // 7 vector<int>::difference_type diff = ivec.end() - ivec.begin(); cout << diff << endl; // 10 } // string 对象使用迭代器 int main() { string a("abcdefg"); for(string::iterator b = a.begin(); b!=a.end(); b++) cout << *b << " "; // 逐个字符输出 }(1) 每种容器都定义了一对命名为 begin 和 end 的函数,用于返回迭代器。 begin() 返回的迭代器指向第一个元素: vector<int>::iterator iter = ivec.begin(); // ivec[0] end() 返回的迭代器指向 vector 的“末端元素的下一个”。 “超出末端迭代器”(off-the-end iterator),表明它指向了一个不存在的元素。 如果 vector 为空,begin 返回的迭代器与 end 返回的迭代器相同。(2) vector 迭代器的自增和解引用运算 迭代器类型可使用解引用操作符(dereference operator)(*)来访问迭代器所指向的元素: *iter = 0; 迭代器使用自增操作符 ++ 指向容器中下一个元素。 end() 返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。 (3) 用 == 或 != 操作符来比较两个迭代器 如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。(4) const_iterator 每种容器类型还定义了一种名为 const_iterator 的类型,用于只读元素的值,防止修改元素的值。 // const 的 iterator -- 可以改变元素的值 vector<int> nums(10); const vector<int>::iterator cit = nums.begin(); *cit = 1; // 可以改变元素的值 ++cit; // 错误!不能改变 const 的 iterator // const_iterator -- 不能改变元素的值 vector<int> nines(10, 9); vector<int>::const_iterator it = nines.begin(); *it = 10; // 错误!因为 *it 是 const ++it; // 可以改变 const_iterator 对象 nines.push_back(10); // 可以改变元素的值 nines[0] = 10; // 可以改变元素的值 // const 的 vector -- 不能被改变元素(包括添加、删除元素,修改元素的值) const vector<int> nines(10, 9); nines.push_back(10); // 错误!因为 nines 是 const nines[0] = 10; // 错误!nines[0] 也是 const const vector<int>::iterator cit2 = nines.begin(); // 错误! // const 的 iterator 可以改变元素的值,然而 nines.begin() 是 const 用 const_iterator 对象遍历。 for (vector<string>::const_iterator iter = text.begin(); iter != text.end(); ++iter) cout << *iter << endl; // 打印元素的值 for (vector<string>::const_iterator iter = text.begin(); iter != text.end(); ++ iter) *iter = " "; // 错误!因为 *iter 是 const(5) 迭代器的算术操作(iterator arithmetic) iter + n // 得到新的迭代器(n 为整型值) iter - n // 得到新的迭代器(n 为整型值) iter1 - iter2 // 两个迭代器相隔的元素个数,得到 difference_type 类型值,实际上就是整型值(6) 任何改变 vector 长度的操作都会使已存在的迭代器失效。 例如,在调用 push_back 之后,就不能再信赖指向 vector 的迭代器的值了。3.5. 标准库 bitset 有些程序要处理二进制位的有序集,每个位可能包含 0(关)1(开)值。 位是用来保存一组项或条件的 yes/no 信息(有时也称标志)的简洁方法。 标准库提供的 bitset 类简化了位集的处理。 #include <bitset> // for bitset using std::bitset;3.5.1. bitset 对象的定义和初始化 类似于 vector,bitset 类也是一种类模板。 在定义 bitset 时,要明确 bitset 含有多少位,须在尖括号内给出它的长度值: bitset<n> b; b 有 n 位,每位都 0 bitset<n> b(u); b 是 unsigned long 型 u 的一个副本 bitset<n> b(s); b 是 string 对象 s 中含有的位串的副本 bitset<n> b(s, pos, n); b 是 s 中从位置 pos 开始的 n 个位的副本。 bitset<32> bitvec; // 32 位,默认全为0 长度值值必须定义为整型字面值常量或是已用常量值初始化的整型的 const 对象。(1) 用 unsigned 值初始化 bitset 对象 bitset<16> bitvec1(0xbbbbfffff); // 位 0~15 初始化为 1 ,超过1长度的高阶位 bbbb 被抛弃 bitset<32> bitvec2(0xfffffffff); // 位 0~31 初始化为 1 bitset<128> bitvec3(0xffffffff); // 位 0~31 初始化为 1 ,位 32~127 为 0(2) 用 string 对象初始化 bitset 对象 // 从 string 对象读入位集的顺序是从右向左 string strval("1100"); bitset<32> bitvec4(strval); // 位 2、3 为 1 ,其他位全为0 string str("1111111000000011001101"); bitset<32> bitvec5(str, 5, 4); // 从 str[5] 开始取 4 位:1100 bitset<32> bitvec6(str, str.size() - 4); // 最后面 4 位:11013.5.2. bitset 对象上的操作bitset 操作 b.any() b 中有值为 1 的二进制位吗? b.none() b 中没有值为 1 的二进制位吗? b.count() b 中置为 1 的二进制位个数 b.size() b 中二进制位的个数 b[pos] 访问 b 中在 pos 处的二进制位 b.test(pos) b 中 第 pos 个二进制位是 1 吗? b.set() 把 b 中所有位都置为 1 b.set(pos) 把 b 中第 pos 个二进制位置为 1 b.reset() 把 b 中所有位都置为 0 b.reset(pos) 把 b 中第 pos 个二进制位置为 0 b.flip() 把 b 中所有二进制位逐位取反 b.flip(pos) 把 b 中第 pos 个二进制位取反 b.to_ulong() 用 b 中同样的二进制位返回一个 unsigned long 值 os << b 把 b 中的位集输出到 os 流 (1) 访问 bitset 对象中的位 // 将 bitvec 中的 32 位全置为 1 for (int index = 0; index != 32; index += 2) bitvec[index] = 1; // 将 bitvec 中的 32 位全置为 1 for (int index = 0; index != 32; index += 2) bitvec.set(index);(2) 测试整个 bitset 对象 bitset<32> bitvec; // 32 位,全为 0 bool is_set = bitvec.any(); // false bool is_not_set = bitvec.none(); // true bitvec[5] = 1; size_t bits_set = bitvec.count(); // 返回 1 size_t sz = bitvec.size(); // 返回 32 size_t 类型定义在 cstddef 头文件中,C 标准库的头文件是 stddef.h。 size_t 类型是一个与机器相关的 unsigned 类型,其大小足以保证存储内在中对象的大小。 if (bitvec.test(i)) // bitvec[i] is on if (bitvec[i]) // bitvec[i] is on(3) 对整个 bitset 对象进行设置 bitvec.reset(); // 所有位全清 0. bitvec.set(); // 所有位全置为 1(4) flip 操作可以对 bitset 对象的所有位或个别位取反: bitvec.flip(0); // 第0位取反 bitvec[0].flip(); // 第0位取反 bitvec.flip(); // 所有位都取反(5) 获取 bitset 对象的值 unsigned long ulong = bitvec3.to_ulong(); cout << "ulong = " << ulong << endl; to_ulong 操作主要用于把 bitset 对象转到 C 风格或标准 C++ 之前风格的程序上。 如果 bitset 对象包含的二进制位数超过 unsigned long 长度,将会产生运行时异常。 在 6.13 节介绍异常(exception),并在 17.1 节中详细地讨论它。(6) 输出二进制位 bitset<32> bitvec2(0xffff); // 位 0~15 置为 1 ,16~31 为 0 cout << "bitvec2: " << bitvec2 << endl; // bitvec2: 00000000000000001111111111111111(7) 使用位操作符 bitset 类也支持内置的位操作符。5.3 节将介绍这些操作符。第4章 数组和指针 C++ 语言提供了两种类似于 vector 和迭代器类型的低级复合类型——数组和指针。 与 vector 类型相似,数组也可以保存某种类型的一组对象; 而它们的区别在于,数组的长度是固定的,如果要在数组中添加新元素, 必须重新分配一块更大的内存块,然后把原数组的所有元素复制到新内存块中。 C++ 程序应该更多地使用 vector 来取代数组,只有 vector 无法达到速度要求时,才使用数组。 指针则可以像迭代器一样用于遍历和检查数组中的元素。4.1. 数组(1) 数组的定义 数组的维
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -