
本文共 14502 字,大约阅读时间需要 48 分钟。
本文为《C++ Primer》的读书笔记
目录
现代的C++程序应当尽量使用
vector
和迭代器,避免使用内置数组和指针;应该尽量使用string
, 避免使用 C 风格的基于数组的字符串
标准库类型 string
#includeusing std::string;
- 标准库类型
string
表示可变长的字符序列
为了与 C 兼容,C++ 中的字符串宇面值是字符数组而非标准库类型
string
的对象
定义和初始化 string
对象
string s1; // 默认初始化,s1是个空串string s2(s1); // **直接初始化**。s2是s1的副本string s3("value"); // s3是字面值"value"的副本 (除了字面值最后的那个空字符)string s4(n, 'c'); // 把 s4 初始化为由连续 n 个字符 c 组成的串string s2 = s1; // **拷贝初始化**。s2是s1的副本string s3 = "value"; // s3是字面值"value"的副本 (除了字面值最后的那个空字符)string s8 = string(n, 'c'); // 显式地创建临时对象用于拷贝
- 当初始值只有一个时,使用直接初始化或拷贝初始化都行。 如果像上面的
s4
那样初始化要用到的值有多个, 一般来说只能使用直接初始化的方式
直接初始化其实是要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数,而拷贝初始化则是使用拷贝构造函数
string
对象上的操作
利用流运算符读写 string
对象
- 在执行读取操作时,
string
对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读起, 直到遇见下一处空白为止
string s;cin >> s;cout << s << endl;
使用 getline
读取一整行
getline
从给定的输入流中读入内容, 直到遇到换行符为止(换行符也被读进来了), 然后把所读的内容存入到string
对象中去(注意不存换行符)。如果输入一开始就是换行符, 那么所得的结果是个空string
- 和输入运算符一样,
getline
也会返回它的流参数
int main(){ string line; //每次读入一整行, 直至到达文件末尾 while (getline(cin, line)) cout << line << endl; return 0;}
empty
成员函数
// 每次读入一整行, 遇到空行直接跳过while (getline(cin, line)) if (!line.empty()) cout << line << endl;
size
成员函数
// 每次读入一整行,输出其中超过80个字符的行while (getline(cin, line)) if (line.size() > 80) cout << line << endl;
size
返回string::size_type
类型的值。它是一个无符号类型的值而且能足够存放下任何string
对象的大小 (实际上就是unsigned int
的类型别名)
auto len = line.size(); // len 的类型是 string::size_type
- 由于
string::size_type
是一个无符号整型数,如果在表达式中混用了带符号数和无符号数将可能产生意想不到的结果。因此切记,如果一条表达式中已经有了size()
就不要再使用int
了- 例如,假设
n
是一个具有负值的int
, 则表达式s.size() < n
的判断结果几乎肯定是true
。这是因为负值n
会自动地转换成一个比较大的无符号值。
- 例如,假设
string
类及其他大多数标准库类型都定义了几种配套的类型。这些配套类型体现了标准库类型与机器无关的特性
比较 string
对象
- 如果两个
string
对象的长度不同, 而且较短string
对象的每个字符都与较长string
对象对应位置上的字符相同, 则较短string
对象小于较长string
对象 - 如果两个
string
对象在某些对应的位置上不一致, 则string
对象比较的结果其实是string
对象中第一对相异字符比较的结果
两个 string
对象相加
- 两个
string
对象相加得到一个新的string
对象,其内容是把左侧的运算对象与右侧的运算对象串接而成 - 另外,
+=
负责把右侧string
对象的内容追加到左侧string
对象的后面 - 标准库允许把字符字面值和字符串字面值转换成
string
对象, 所以在需要string
对象的地方就可以使用这两种字面值来替代。当把string
对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个+
的两侧的运算对象至少有一个是string
string s1 = "hello", s2 = "world"; //在sl和s2中都没有标点符号string s3 = s1 + ", " + s2 +'\n';
string s5 = "hello" + ", "; //错误: 两个运算对象都不是stringstring s6 = s1 + ", " + "world"; //正确:每个加法运算符都有一个运算对象是stringstring s7 = "hello" + ", " + s2; //错误
下标运算符
- 下标运算符
[]
接收的输入参数是string::size_type
类型的值,表示要访问的字符的位置; - 返回值是该位置上字符的引用
string
对象的下标必须大于等于0
而小于s.size()
(s[s.size()-1]
是最后一个字符)
处理 string
对象中的字符
#include
处理每个字符
- 可以使用 范围
for
语句 或 与之等价的使用迭代器的传统for
语句
// 把string对象中的字符每行一个输出出来string str("some string");// 这里c的类型是charfor (const auto &c : str) cout << c << endl;
// 统计string对象中标点符号的个数string s("Hello World!!!");// punct_cnt的类型和s.size的返回类型一样decltype(s.size()) punct_cnt = 0;for (const auto &c : s) if (ispunct(c)) ++punct_cnt;cout << punct_cnt << " punctuation characters in "<< s << endl;
//把字符串改写为大写字母的形式string s("Hello World'!!");for (auto &c : s) c = toupper(c);cout << s << endl;
处理部分字符
要想访问string
对象中的单个字符有两种方式:
- 下标
- 迭代器
//输出string对象中的第一个字符:if (!s.empty()) cout << s[0] << endl;
//将字符串的首字符改成了大写形式:string s("some string");if (!s.empty()) s[0] = toupper(s[0]);
// 把s的第一个词改成大写形式:// 依次处理s中的宇符直至我们处理完全部宇符或者遇到一个空白for (decltype(s.size()) index = 0; index != s.size() && !isspace(s[index]); ++index) s[index] = toupper(s[index]);
// 把0到15之间的十进制数转换成对应的十六进制形式const string hexdigits = "0123456789ABCDEF"; //可能的十六进制数字cout << "Enter a series of numbers between 0 and 15" <<" separated by spaces. Hit ENTER when finished: " << endl;string result; //用于保存十六进制的宇符串string::size_type n; //用于保存从输入流读取的数while (cin >> n) if (n < hexdigits.size()) //忽略无效输入 result += hexdigits[n]; //得到对应的十六进制数字cout << "Your hex number is: " << result << endl;
额外的string
操作
- 除了顺序容器共同的操作之外,
string
类型还提供了一些额外的操作。这些操作中的大部分要么是提供string
类和 C 风格字符数组之间的相互转换,要么是增加了允许我们用下标代替迭代器的版本
构造 string
的其他方法
- 除了之前已经介绍过的构造函数,以及与其他顺序容器相同的构造函数外,
string
类型还支持另外三个构造函数
- 通常当我们从一个
const char*
创建string
时, 指针指向的数组必须以空字符结尾, 拷贝操作遇到空字符时停止。如果我们还传递给构造函数一个计数值, 数组就不必以空字符结尾。如果我们未传递计数值且数组也未以空字符结尾, 或者给定计数值大于数组大小, 则构造函数的行为是未定义的 - 当从一个
string
拷贝字符时, 我们可以提供一个可选的开始位置和一个计数值。开始位置必须小于或等于给定的string
的大小。如果位置大于size
, 则构造函数抛出一个out_of_range
异常。如果我们传递了一个计数值, 则从给定位置开始拷贝这么多个字符。不管我们要求拷贝多少个字符,标准库最多拷贝到string
结尾, 不会更多
const char *cp = "Hello World!!!"; //以空宇符结束的数组char noNull[] = { 'H', 'i'}; //不是以空宇符结束string s1(cp); //拷贝cp中的字符直到遇到空宇符; sl == "Hello World!!!"string s2(noNull,2); //从noNull拷贝两个字符; s2 == "Hi"string s3(noNull); //未定义: noNull不是以空字符结束string s4(cp + 6, 5); // 从cp[6]开始拷贝5个字符; s4 == "World"string s5(s1, 6, 5); // 从s1[6]开始拷贝5个字符; s5 == "World"string s6(s1, 6); // 从s1[6]开始拷贝,直至s1末尾; s6 =="World'!!"string s7(s1, 6, 20); // 正确, 只拷贝到s1末尾; s7 == "World!!!"string s8(s1, 16); // 抛出一个out_of_range异常
substr
操作
- 如果开始位置超过了
string
的大小,则substr
函数抛出一个out_of_range
异常。如果开始位置加上计数值大于string
的大小, 则substr
会调整计数值, 只拷贝到string
的末尾
string s("hello world");string s4 = s.substr(6, 11); // s4 = worldstring s5 = s.substr(12); // 抛出一个out_of_range异常
改变 string
的其他方法
string
支持顺序容器的赋值运算符以及assign
、insert
和erase
操作。除此之外, 它还定义了额外的insert
和erase
版本
string str = "abcxyz", str2 = "opq";str.insert(str.begin() + 3, str2.begin(), str2.end()); // abcopqxyz
- 除了接受迭代器的
insert
和erase
版本外,string
还提供了接受下标的版本。下标指出了开始删除的位置, 或是insert
到给定值之前的位置:
s.insert(s.size(), 5, '!'); // 在 s 末尾插入 5 个感叹号s.erase(s.size() - 5, 5); // 从 s 删除最后 5个字符
string
还提供了接受 C 风格字符数组的insert
和assign
版本- 例如, 我们可以将以空字符结尾的字符数组
insert
到或assign
给一个string
:
- 例如, 我们可以将以空字符结尾的字符数组
const char *cp = "Stately, plump Buck";s.assign(cp, 7); // s == "Stately" 通过调用 `assign` 替换 `s` 的内容s.insert(s.size(), cp + 7); // s == "Stately, plump Buck"
- 我们也可以指定将来自其他
string
或子字符串的字符插入到当前string
中或赋予当前string
:
string s = "some string", s2 = "some other string";s.insert(0, s2); //在 s[0] 之前插入 s2 的拷贝// 在 s[0] 之前插入 s2 中 s2[0] 开始的 s2.size() 个字符s.insert(0, s2, 0, s2.size());
append
和 replace
函数
string
类定义了两个额外的成员函数:append
和replace
append
操作是在string
末尾进行插入操作的一种简写形式:
string s("C++ Primer"), s2 = s; //将s和s2初始化为"C++ Primer"s.insert(s.size(), " 4th Ed.");//s == "C++ Primer 4th Ed."s2.append(" 4th Ed."); //等价方法:将" 4th Ed."追加到s2; s == s2
replace
操作是调用erase
和insert
的一种简写形式:
// 将"4th"替换为"5th"的等价方法s.erase(11, 3); //s == "C++ Primer Ed."s.insert(11, "5th"); //s == "C++ Primer 5th Ed."// 从位置11开始, 删除3 个字符并插入"5th"s2.replace(11, 3, "5th"); // 等价方法:s == s2
- 此例中调用
replace
时, 插入的文本恰好与删除的文本一样长。这不是必须的, 可以插入一个更长或更短的string
:
s.replace(11, 3, "Fifth"); //s == "C++ Primer Fifth Ed."
string
搜索操作
string
类提供了6 个不同的搜索函数,每个函数都有 4 个重载版本
- 每个搜索操作都返回一个
string::size_type
值, 表示匹配发生位置的下标。如果搜索失败, 则返回一个名为string::npos
的static
成员。标准库将npos
定义为一个const string::size_type
类型,并初始化为值-1
。由于npos
是一个unsigned
类型, 此初始值意味着npos
等于任何string
最大的可能大小
W A R N I N G WARNING WARNING:
string
搜索函数返回string:: size_type
值, 该类型是一个unsigned
类型。因此, 用一个int
或其他带符号类型来保存这些函数的返回值不是一 个好主意
string name("AnnaBelle");auto posl = name.find("Anna"); // posl == 0
指定在哪里开始搜索
- 我们可以传递给
find
操作一个可选的开始位置pos
。这个可选的参数指出从哪个位置开始进行搜索。默认情况下, 此位置被置为0。一种常见的程序设计模式是用这个可选参数在字符串中循环地搜索子字符串出现的所有位置:
string::size_type pos = 0;// 每步循环查找name中下一个数while ((pos = name.find_first_of(numbers, pos)) != string::npos){ cout << "found number at index: " << pos << " element is " << name[pos] << endl; ++pos; //移动到下一个字符}
compare
函数
除了关系运算符外,标准库string
类型还提供了一组compare
函数,这些函数与C标准库的strcmp
函数很相似, 根据s
是等于、大于还是小于参数指定的字符串, s.compare
返回0 、正数或负数
如表9.15所示, compare
有6个版本:

数值转换
字符串中常常包含表示数值的字符。新标准引入了多个函数, 可以实现数值数据与标准库string
之间的转换:
int i = 42;string s = to_string(i); //将整数i转换为宇符表示形式double d = stod(s); //将字符串s转换为浮点数
要转换为数值的string
中第一个非空白符必须是数值中可能出现的字符:
string s2 = "pi = 3.14";// 转换s中以数宇开始的第一个子串, 结果d = 3.14d = stod(s2.substr(s2.find_first_of("+-.0123456789")));
string
参数中第一个非空白符必须是符号(+
或-
)或数字。它可以以0X
或0x
开头来表示十六进制数。对那些将字符串转换为浮点值的函数, string
参数也可以以小数点开头, 并可以包含e
或E
来表示指数部分。对于那些将字符串转换为整型值的函数, 根据基数不同, string
参数可以包含字母字符, 对应大于数字9的数。
如果
string
不能转换为一个数值, 这些函数抛出一个invalid_argument
异常。如果转换得到的数值无法用任何类型来表示,则抛出一个out_of_range
异常
标准库类型 vector
#includeusing std::vector;
vector
表示对象的集合, 其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引用于访问对象vector
是一个类模板,因此必须要提供额外信息(必须显式提供模板实参)来指定模板到底实例化成什么样的类,方式是在模板名字后面跟一对尖括号, 在括号内放上信息。vector
需要提供的额外信息是vector
内所存放对象的类型vector
能容纳绝大多数类型的对象作为其元素, 但是因为引用不是对象, 所以不存在包含引用的vector
因为
vector
"容纳着“ 其他对象, 所以它也常被称作容器(container)
vector ivec;vectordvec;vector > file;
在早期版本的C++标准中如果
vector
的元素还是vector
(或者其他模板类型),则必须在外层vector
对象的右尖括号和其元素类型之间添加一个空格, 如应该写成vector<vector<int> >
定义和初始化 vector
对象
默认初始化
- 最常见的方式就是利用默认初始化,先定义一个空
vector
, 然后当运行时获取到元素的值后再逐一添加 (程序在运行时可以很高效地利用push_back
向vector
对象中添加元素)
vector对象能高效增长
- C++标准要求
vector
应该能在运行时高效快速地添加元素。因此既然vector
对象能高效地增长, 那么在定义vector
对象的时候设定其大小也就没什么必要了,事实上如果这么做性能可能更差。只有一种例外情况, 就是所有元素的值都一样 - 一旦元素的值有所不同, 更有效的办法是先定义一个空的
vector
对象, 再在运行时向其中添加具体值
直接初始化
初始化为另一个 vector
的副本
- 需要注意:两个
vector
对象的类型必须相同
第二个是拷贝初始化,但为了方便就把它跟直接初始化放一起了
初始化为包含 n n n 个重复的元素
值初始化
- 可以只提供
vector
对象容纳的元素个数而略去初始值。此时库会创建一个值初始化的元素初值, 并把它赋给容器中的所有元素 - 这个初值由
vector
对象中元素的类型决定- 如果
vector
对象的元素是内置类型, 比如int
, 则元素初始值自动设为0
- 如果元素是某种类类型, 比如
string
, 则元素由类的默认构造函数进行初始化。因此,如果该类没有默认构造函数,则不能使用值初始化,必须显式提供初始元素值
- 如果
使用数组初始化 vector
对象
- 只要指明要拷贝区域的首元素地址和尾后地址即可
int int_arr[] = { 0, 1, 2, 3, 4, 5};vector ivec(begin(int_arr), end(int_arr));
列表初始化
vectorv1{ "a", "an", "the"}; //列表初始化
不同初始化方式的区别
- 列表初始值还是元素数量?
vector v1(10); // v1有10个元素, 每个的值都是0vector v2{ 10}; // v2有1个元素, 该元素的值是10vector v3(10, 1); // v3有10个元素, 每个的值都是1vector v4{ 10, 1}; // v4有2个元素, 值分别是10和1
- 如果用的是圆括号, 可以说提供的值是用来构造(construct)
vector
对象的 - 如果用的是花括号, 可以表述成我们想列表初始化该
vector
对象。也就是说,初始化过程会尽可能地把花括号内的值当成是元素初始值的列表来处理, 只有在无法执行列表初始化时才会考虑其他初始化方式。如果初始化时使用了花括号的形式但是提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector
对象了
vectorv5{ "hi"}; //列表初始化: v5有一个元素vector v6("hi"); //错误: 不能使用字符串字面值构建vector对象vector v7{ 10}; // v7有10个默认初始化的元素vector v8{ 10, "hi" }; //v8有10个值为"hi"的元素
向 vector
对象中添加元素
push_back
负责把一个值 “压到”(push)vector
对象的 “尾端”(back)
// 从标准输入中读取单词, 将其作为vector对象的元素存储string word;vectortext;while (cin >> word) text.push_back(word);
某些对vector
对象的操作会使迭代器失效
- 任何一种可能改变
vector
对象容量的操作, 比如push_back
, 都会使该vector
对象的迭代器失效 - 因此,使用了迭代器的循环体 (范围
for
循环), 都不要向迭代器所属的容器添加元素
其他 vector
操作
v.front()
:返回当前vector
容器中起始元素的引用v.back()
:返回当前vector
容器中末尾元素的引用v.pop_back()
:高效销毁vector
容器的末尾元素v.clear()
: 情况vector
内所有元素。时间复杂度为 O ( n ) O(n) O(n)v.insert(it, x)
: 向v
的任意迭代器it
处插入一个元素x
,原来在这个位置及之后的元素都相后移。时间复杂度为 O ( n ) O(n) O(n)v.erase(it)
: 删除迭代器it
处的元素;v.erase(first, end)
: 删除[first, last)
内的所有元素。时间复杂度均为 O ( n ) O(n) O(n)
- 可以使用范围
for
语句处理vector
对象中的所有元素:
vector v{ 1,2,3,4,5,6,7,8,9};for (auto &i : v) i *= i; for (const auto &i : v) cout << i << " ";
size
返回值的类型是由vector
定义的size_type
类型。要使用size_type
, 需首先指定它是由哪种类型定义的:
vector ::size_type //正确vector::size_type //错误
vector
的关系运算符规则类似于string
:- 两个
vector
对象相等当且仅当它们所含的元素个数相同, 而且对应位置的元素值也相同 - 如果两个
vector
对象的容量不同,但是在相同位置上的元素值都一样,则元素较少的vector
对象小于元素较多的vector
对象 - 若元素的值有区别,则
vector
对象的大小关系由第一对相异的元素值的大小关系决定 - 但要注意:只有当
vector
中元素的值可比较(支持对应的关系运算符)时,vector
对象才能被比较
- 两个
//以10分为一个分数段统计成绩的数量: 0-9, 10-19, ... , 90-99, 100vectorscores(11, 0) ; /// 11 个分数段, 全都初始化为0unsigned grade;while (cin >> grade) if (grade <= 100) ++scores[grade/10];
迭代器 (iterator)
- 所有标准库容器都可以使用迭代器,但是其中只有少数几种才同时支持下标运算符。因此,从泛型编程的角度来看,使用迭代器比使用下标更好
严格来说,
string
不属于容器类型,但是string
支持很多与容器类型类似的操作(string
支持迭代器)
- 迭代器提供了对对象的间接访问。就迭代器而言,其对象是容器中的元素或者
string
对象中的字符 - 迭代器有有效和无效之分,这一点和指针差不多。有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置;其他所有情况都属于无效
迭代器范围
一个迭代器范围(iterator range)由一对迭代器begin
和end
表示。两个迭代器begin
和end
必须满足:
- 它们指向同一个容器中的元素,或者是容器最后一个元素之后的位置,且我们可以通过反复递增
begin
来到达end
。即end
不在begin
之前
编译器不会强制这些要求。确保程序符合这些约定是程序员的责任
迭代器的类型
- 我们认定某个类型是迭代器当且仅当它支持一套操作, 使得我们能访问容器的元素或者从某个元素移动到另外一个元素
- 拥有迭代器的标准库类型使用
iterator
和const_iterator
来表示迭代器的类型, 这两个类型支持迭代器概念所规定的一套操作const_iterator
和常量指针差不多, 能读取但不能修改它所指的元素值- 如果
vector
对象或string
对象是一个常量, 只能使用const_iterator
; 如果vector
对象或string
对象不是常量,那么既能使用iterator
也能使用const_iterator
vector ::iterator it;string::iterator it2;vector ::const_iterator it3; // it3只能读元素, 不能写元素string::const_iterator it4; // it4只能读字符, 不能写字符
begin
, end
, cbegin
, cend
- 有迭代器的类型同时拥有返回迭代器的成员。比如,这些类型都拥有名为
begin
和end
的成员begin
成员负责返回首迭代器,即指向第一个元素的迭代器end
成员则负责返回尾后迭代器 (尾迭代器),即指向容器(或string
) 尾元素的下一位置的迭代器- 如果容器为空,则
begin
和end
返回的都是尾后迭代器
// b 表示v的第一个元素, e 表示v尾元素的下一位置auto b = v.begin(), e = v.end();
begin
和end
返回的具体类型由对象是否是常量决定, 如果对象是常量, 返回const_iterator
; 否则返回iterator
vector v;const vector cv;auto it1 = v.begin(); // it1的类型是vector ::iteratorauto it2 = cv.begin() ; //it2的类型是vector ::const_iterator
- 有时候这种默认的行为并非我们所要。如果对象只需读操作而无须写操作的话最好使用
const_iterator
。为了便于专门得到const_iterator
类型的返回值,C++11新标准引入了两个新函数,cbegin
和cend
:
auto it3 = v.cbegin(); // it3的类型是vector ::const_iterator
使用左闭合范围蕴含的编程假定
标准库使用左闭合范围是因为这种范围有三种方便的性质。假定begin
和end
构成一个合法的迭代器范围,则
- 如果
begin
与end
相等,则范围为空 - 如果
begin
与end
不等,则范围至少包含一个元素,且begin
指向该范围中的第一个元素 - 我们可以对
begin
递增若干次,使得begin == end
这些性质意味着我们可以像下面的代码一样用一个循环来处理一个元素范围,而这是安全的:
// 注意,对迭代器尽量使用 != (泛型编程思想)while (begin != end) { *begin = val; ++begin; }
迭代器运算符
与容器一样,迭代器有着公共接口:如果一个迭代器提供某个操作,那么所有提供相同操作的迭代器对这个操作的实现方式都是相同的
- 例如,标准容器类型上的所有迭代器都允许我们访问容器中的元素,而所有迭代器都是通过解引用运算符来实现这个操作的
- 类似的,标准库容器的所有迭代器都定义了递增运算符
标准容器迭代器共有的运算符
有一个例外不符合公共接口特点:
forward_list
迭代器不支待递减运算符--
(单向链表类,为了保证执行效率,不提供--
操作)
string s("some string");if (s.begin() != s.end()) { //确保s非空 auto it = s.begin(); //it表示s的第一个宇符 *it = toupper(*it); //将当前字符改成大写形式}
//将第一个单词改成大写形式//依次处理s的字符直至我们处理完全部字符或者遇到空白for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it) *it = toupper(*it); //将当前字符改成大写形式
泛型编程
原来使用 C 的程序员在转而使用C++语言之后,会对for
循环中使用!=
而非<
进行判断有点儿奇怪。C++程序员习惯性地使用!=
,其原因和他们更愿意使用迭代器而非下标的原因一样:这种编程风格在标准库提供的所有容器上都有效。所有标准库容器的迭代器都定义了==
和!=
, 但是它们中的大多数都没有定义<
运算符。因此, 只要我们养成使用迭代器和!=
的习惯, 就不用大在意用的到底是哪种容器类型
- 箭头运算符
->
// 依次输出text的每一行直至遇到笫一个空白行为止for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it) cout << *it << endl;
某些特定类型支持的迭代器算术运算
string
、vector
、deque
和array
迭代器支持的算术运算:
iter + n
:返回是向后移动了若个位置的迭代器
// 计算得到最接近vi中间元素的一个迭代器auto mid = vi.begin() + vi.size() / 2;
iter - n
iter += n
iter -= n
iter1 - iter2
:只要两个迭代器指向的是同一个容器中的元素或者尾元素的下一位置, 就能将其相减, 所得结果是两个迭代器的距离。其类型是名为difference_type
的带符号整型数。string
和vector
都定义了difference_type
>
,>=
,<
,<=
:参与比较的两个迭代器必须合法而且指向的是同一个容器的元素(或者尾元素的下一位置)
即使迭代器没有定义
+
/-
运算符,也会有++
/--
运算符,因此,应尽量使用++
/--
运算符
- 二分搜索
// text必须是有序的// beg和end表示我们搜索的范围auto beg = text.begin(), end = text.end();auto mid = text.begin() + (end - beg)/2; //当还有元素尚未梒查并且我们还没有找到sought时执行循环while (mid != end && *mid != sought) { if(sought < *mid) //我们要找的元素在前半部分吗? end = mid; //如果是,调整搜索范围使得忽略掉后半部分 else //我们要找的元素在后半部分 beg = mid + 1; //在mid之后寻找 mid = beg + (end - beg) /2; //新的中间点}//循环过程终止时,mid或者等于end或者指向要找的元素。如果mid等于end, 说明text中没有我们要找的元素
发表评论
最新留言
关于作者
