C++基础(四):string、vector、迭代器
发布日期:2021-05-07 06:30:35 浏览次数:25 分类:精选文章

本文共 14502 字,大约阅读时间需要 48 分钟。

本文为《C++ Primer》的读书笔记

目录

现代的C++程序应当尽量使用vector和迭代器,避免使用内置数组和指针;应该尽量使用string, 避免使用 C 风格的基于数组的字符串

标准库类型 string

#include 
using 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支持顺序容器的赋值运算符以及 assigninserterase 操作。除此之外, 它还定义了额外的 inserterase 版本

在这里插入图片描述

string str = "abcxyz", str2 = "opq";str.insert(str.begin() + 3, str2.begin(), str2.end());	// abcopqxyz
  • 除了接受迭代器的inserterase版本外, string还提供了接受下标的版本。下标指出了开始删除的位置, 或是insert到给定值之前的位置:
s.insert(s.size(), 5, '!'); // 在 s 末尾插入 5 个感叹号s.erase(s.size() - 5, 5); // 从 s 删除最后 5个字符
  • string还提供了接受 C 风格字符数组的insertassign版本
    • 例如, 我们可以将以空字符结尾的字符数组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());

appendreplace 函数

  • string类定义了两个额外的成员函数: appendreplace

在这里插入图片描述

  • 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操作是调用eraseinsert的一种简写形式:
// 将"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::nposstatic 成员。标准库将npos 定义为一个const string::size_type 类型,并初始化为值-1。由于npos 是一个unsigned 类型, 此初始值意味着npos 等于任何string 最大的可能大小

W A R N I N G WARNING WARNINGstring 搜索函数返回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 参数中第一个非空白符必须是符号()或数字。它可以以0X0x开头来表示十六进制数。对那些将字符串转换为浮点值的函数, string参数也可以以小数点开头, 并可以包含eE来表示指数部分。对于那些将字符串转换为整型值的函数, 根据基数不同, string参数可以包含字母字符, 对应大于数字9的数。

如果string不能转换为一个数值, 这些函数抛出一个invalid_argument异常。如果转换得到的数值无法用任何类型来表示,则抛出一个out_of_range异常

标准库类型 vector

#include 
using std::vector;
  • vector表示对象的集合, 其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引用于访问对象
  • vector是一个类模板,因此必须要提供额外信息(必须显式提供模板实参)来指定模板到底实例化成什么样的类,方式是在模板名字后面跟一对尖括号, 在括号内放上信息vector 需要提供的额外信息是vector内所存放对象的类型
  • vector能容纳绝大多数类型的对象作为其元素, 但是因为引用不是对象, 所以不存在包含引用的vector

因为vector "容纳着“ 其他对象, 所以它也常被称作容器(container)

vector
ivec;vector
dvec;vector
> file;

在早期版本的C++标准中如果vector的元素还是vector (或者其他模板类型),则必须在外层vector对象的右尖括号和其元素类型之间添加一个空格, 如应该写成vector<vector<int> >

定义和初始化 vector 对象

在这里插入图片描述

默认初始化

在这里插入图片描述

  • 最常见的方式就是利用默认初始化,先定义一个空vector, 然后当运行时获取到元素的值后再逐一添加
    (程序在运行时可以很高效地利用push_backvector对象中添加元素)

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));

列表初始化

在这里插入图片描述

vector
v1{ "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对象了
vector
v5{ "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;vector
text;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, 100vector
scores(11, 0) ; /// 11 个分数段, 全都初始化为0unsigned grade;while (cin >> grade) if (grade <= 100) ++scores[grade/10];

迭代器 (iterator)

  • 所有标准库容器都可以使用迭代器,但是其中只有少数几种才同时支持下标运算符。因此,从泛型编程的角度来看,使用迭代器比使用下标更好

严格来说,string不属于容器类型,但是string支持很多与容器类型类似的操作(string支持迭代器)

  • 迭代器提供了对对象的间接访问。就迭代器而言,其对象是容器中的元素或者string对象中的字符
  • 迭代器有有效和无效之分,这一点和指针差不多。有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置;其他所有情况都属于无效

迭代器范围

一个迭代器范围(iterator range)由一对迭代器beginend表示。两个迭代器beginend必须满足:

  • 它们指向同一个容器中的元素,或者是容器最后一个元素之后的位置,且我们可以通过反复递增begin来到达end。即end不在begin之前

编译器不会强制这些要求。确保程序符合这些约定是程序员的责任

迭代器的类型

  • 我们认定某个类型是迭代器当且仅当它支持一套操作, 使得我们能访问容器的元素或者从某个元素移动到另外一个元素
  • 拥有迭代器的标准库类型使用iteratorconst_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

  • 有迭代器的类型同时拥有返回迭代器的成员。比如,这些类型都拥有名为 beginend 的成员
    • begin成员负责返回首迭代器,即指向第一个元素的迭代器
    • end成员则负责返回尾后迭代器 (尾迭代器),即指向容器(或string) 尾元素的下一位置的迭代器
    • 如果容器为空,则beginend返回的都是尾后迭代器
// b 表示v的第一个元素, e 表示v尾元素的下一位置auto b = v.begin(), e = v.end();

  • beginend返回的具体类型由对象是否是常量决定, 如果对象是常量, 返回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新标准引入了两个新函数,cbegincend:
auto it3 = v.cbegin(); // it3的类型是vector
::const_iterator

使用左闭合范围蕴含的编程假定

标准库使用左闭合范围是因为这种范围有三种方便的性质。假定beginend构成一个合法的迭代器范围,则

  • 如果beginend相等,则范围为空
  • 如果beginend不等,则范围至少包含一个元素,且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;

某些特定类型支持的迭代器算术运算

stringvectordequearray 迭代器支持的算术运算:

(这些算术运算应该是在顺序存储的时候效率比较高,因此在一些基于链表的数据结构上,就不会支持这些算术运算)

  • iter + n:返回是向后移动了若个位置的迭代器
// 计算得到最接近vi中间元素的一个迭代器auto mid = vi.begin() + vi.size() / 2;
  • iter - n
  • iter += n
  • iter -= n
  • iter1 - iter2:只要两个迭代器指向的是同一个容器中的元素或者尾元素的下一位置, 就能将其相减, 所得结果是两个迭代器的距离。其类型是名为difference_type的带符号整型数。stringvector都定义了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中没有我们要找的元素
上一篇:JAVA关键字
下一篇:CSS的使用方式和优势

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2025年04月17日 02时10分20秒