STL中的string容器和迭代器iterator

前言

这一片博客开始,我们进入STL标准模板库的学习

什么是STL

STL(Standard Template Library)是C++标准库的核心组成部分,提供了一系列通用模板类和函数 ,实现了常见的数据结构和算法。它基于泛型编程思想,强调代码复用和高效性。

STL的版本

STL的六大组件

就像上图所示的一样,STL中一共有六大组件分别是仿函数、算法、迭代器、空间配置器、容器、配接器

今天我们主要来讲一下容器中的string和迭代器中的iterator、const_iterator

STL中的容器string

string - C++ Reference (cplusplus.com)

上面这个链接是C++的一个文档网站,里面有C++中相关的所有内容

这里我们可以看到string其实就是一个类,而且是一个字符串类型的类

string的构造函数

这就是string的构造函数,这里主要的是要去掌握1、2、4具体的3、5可以做一个了解,因为没有前面三个常用

cpp 复制代码
// string 是 basic_string<char> 的重命名
typedef basic_string<char> string;
basic_string<char> s;
int main()
{
	//string();
	string s1;
	s1 = "hello world";

	//string(const string& str)
	string s2(s1);
	string s3 = s2;//调用拷贝函数

	//string(const char* s)
	string s4("hello world");

	//字符串类型可以直接用<<>>来打印和输入
	//string类中有对<<>>的重载
	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
	return 0;
}
cpp 复制代码
int main()
{
	//string(const string& str, size_t pos, size_t len = npos)
	//第三个是缺省值,要是你不输入他会默认给一个-1,size_t的-1就是一个很大的数字了大约是42亿bit~4G左右
	string s1("hello world");
	string s2(s1, 1, 9);
	cout << s2 << endl;

	//string(const char* s, size_t n)
	//这个构造函数的使用方式是:从第一个参数的第一个字符开始拷贝,拷贝n个字符,结束
	//第一个参数必须得是字符串,不能是字符串对象
    //否则就会调用string(const string& str, size_t pos, size_t len = npos)这个构造函数
	string s3("hello world", 5);
	cout << s3 << endl;
	return 0;
}

以上是这几个常用的构造函数创建对象的方式

这里在对npos说明一下,这是string类模版中的静态常量

size_tnpos = -1 ,这会使的npos是一个非常大的值,这个值足以应付我们正常的字符串拷贝

stirng中size()、lenght()的使用

cpp 复制代码
int main()
{
	string s1("hello world");
	size_t _size = s1.size();//size()和lenght()的返回值都是size_t类型的数据
	cout << _size << endl;
	cout << s1.size()<<endl;
	cout << s1.length() << endl;
	return 0;
}

size()、length()俩者没有任何区别,你想使用哪一个都可以,作用就是计算该字符串对象中字符串的长度

string中[ ]的使用

cpp 复制代码
int main() 
{
	string s1("hello world");
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
		
	}
	return 0;
}

可以支持直接访问操作

cpp 复制代码
int main() 
{
	string s1("hello world");
	s1[0] = 'x';
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
		
	}
	return 0;
}

也可以支持赋值操作

\]重载也是支持const修饰的,保证了\[\]在针对const修饰的string对象的时候,不会被修改数据 ### string中at的使用 ![](https://i-blog.csdnimg.cn/direct/cab05ee1bfa24297b8fb31cdd705224e.png) at的作用和\[\]是一样的,但是at是更安全的\[\],它支持越界的检查,我知道\[\]对越界的检查只在为尾元素的后几个,一旦超过一定距离,编译器就不会报错,at就很好的解决了这个问题 ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; void print(const string& str) { for(size_t i = 0;i < str.size(); i++) { cout << str.at(i) << " "; } cout << endl; } int main() { string s("1234567890"); print(s); return 0; } ``` 代码结果: > 1 2 3 4 5 6 7 8 9 0 ### string中back/front的使用 ![](https://i-blog.csdnimg.cn/direct/cfc240e5c917406bac4d3d2ba3271b14.png) ![](https://i-blog.csdnimg.cn/direct/f887daecb36d4875a2c3369751afb69f.png) front是返回string对象中的第一个元素 back是返回string对象中的最后一个元素 俩个const版本也是对应const修饰的string类型对象 ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string s("1234567890"); cout << s.back() << endl; cout << s.front() << endl; return 0; } ``` 代码结果: > 0 > > 1 ### sting中max_size max_size返回的是**该容器对象在理想情况下理论上可以容纳的最大元素个数** 这个方法的作用并没有什么真正的运用场景,因为内存的大小,系统的不同都会影响,所以这个方法可以不看 ### string中capacity ![](https://i-blog.csdnimg.cn/direct/6c6d77c984fe46eca1d66b1637afe4e6.png) 作用是返回string对象的最大存储容量,之前我们就说了其实string就是一个字符串,那么是字符串就会存在**最大存储容量** 和**当前存储个数** 和**起始位置**这三个成员属性 ### string中resize ![](https://i-blog.csdnimg.cn/direct/5120bd0a270a4b8095a4e6283abdc00b.png) 该方法的作用是根据你给的n来调整string对象的大小 > 1.n \> 对象的最大存储容量时(capacity) > > * capacity会扩大到**至少n**(可能略大于n,取决于内存分配策略) > * size会变成n > * 新元素会初始化为指定值(c存在)或默认值(c不存在) > * ⚠️ **注意** :capacity可能**大于**n,不一定精确等于n > > 2.size \< n \< capacity 时 > > * capacity不变 > * size = n > * 新元素会初始化为指定值(c存在)或默认值(c不存在) > > 3.n \< size时 > > * capacity不变 > * size = n > * size - n这段数据丢失 ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string s("1234567890"); cout << s << endl; cout << "s的初始capacity和size" << endl; cout << "capacity:"< capacity" << endl; s.resize(15,'a'); cout << "capacity:" << s.capacity() << endl; cout << "size:" << s.size() << endl; cout << s << endl; cout << endl << endl; cout << "size < n < capacity" << endl; s.resize(11); cout << "capacity:" << s.capacity() << endl; cout << "size:" << s.size() << endl; cout << s << endl; cout << endl << endl; cout << "n < size" << endl; s.resize(5); cout << "capacity:" << s.capacity() << endl; cout << "size:" << s.size() << endl; cout << s << endl; return 0; } ``` 代码结果: ![](https://i-blog.csdnimg.cn/direct/ede50a26ba3b4b72aa4c600bf5d670a1.png) ### string中reserve ![](https://i-blog.csdnimg.cn/direct/7913fb3dd00c4dbdbe740e1d78706bfc.png) reserve的作用就是将string对象的capacity扩容到 大于等于n ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string s("12345"); cout << "capacity:" << s.capacity() << endl; s.reserve(20); cout << "reserve->capacity:" << s.capacity() << endl; s.reserve(20); cout << "reserve->capacity:" << s.capacity() << endl; return 0; } ``` 代码结果: ![](https://i-blog.csdnimg.cn/direct/404dd980661c486188f5f3313bd63e20.png)特点: > 当 n 小于capacity的时候,string对象的capacity不会改变 > > 当 n 大于capacity的时候,string对象的capacity会增容到大于等于n > > **不同的编译器下的reserve的实现机制不同,在vs2019下的reserve的增容不是以n** > > **这个数值为基准的,他会在n的基础上进行对齐操作,所以最后增容的大小其实并不是n** > > **但是在Linux下,reserve就是按照n的大小来进行扩容,并且最后的增容大小也是以n为基准** > > **重点:不同的编译器下的reserve的实现不同** ### clear ![](https://i-blog.csdnimg.cn/direct/748054102c96404e926ecdef200a47d8.png) clear的作用是清空string对象的数据,将size置为0,capacity不变 ![](https://i-blog.csdnimg.cn/direct/655c59d80e1c4491bcfd3d87869dcfb8.png) ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string s("12345"); cout << s << endl; s.clear(); cout << "clear:" << s << endl; return 0; } ``` 代码结果: ![](https://i-blog.csdnimg.cn/direct/771c0aca938e405e8467c74d41a7b20c.png) ### empty ![](https://i-blog.csdnimg.cn/direct/ab4855c9629e4f2ab457c42378f36ba3.png) 作用是判断string是否为空,为空就返回true,不为空就返回false ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string s("12345"); cout << s.empty() << endl; s.clear(); cout << "clear:" << s.empty() << endl; return 0; } ``` 代码结果: ![](https://i-blog.csdnimg.cn/direct/88fa35a8298b45c8a92618ddff9004b7.png) ### += ![](https://i-blog.csdnimg.cn/direct/764b60495a964277b53a2f5b3d648d5d.png) 我们看到+=有三个版本,其实版本1可以不用设计,因为c++中有隐式转化 +=就是对string对象进行尾差 ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string s("12345"); string ss = "one two three"; cout << s<< endl; s += " 6789 "; s += " qwe"; s += ' c '; s += ss; cout < ![](https://i-blog.csdnimg.cn/direct/ded54ab595af477fa000074b9129794d.png) > > 也可以是目标数据str的一部分(subpos-\>sublen)进行尾插 > > **这个版本的append在使用的过程你可能觉得他是左闭右开的,其实不是。如果你传的是0-size这个区间,那么其实最后是有把\\0也插入进去的,因为\\0对于string来说就是一个普通的字符而已,只是在打印的时候会以\\0作为结束标志** > > **如果区间超过了源数据,这种行为是C++未定义的行为,编译器不回去自动检查你的append有没有发生越界** > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > > int main() > { > string s("12345"); > string ss("ont two three"); > cout << "ss.len:" << ss.size() << endl; > s.append(ss, 0, ss.size()); > cout << s << endl; > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/da08454d68174bd68b78dc2c72fdd23b.png) > > ![](https://i-blog.csdnimg.cn/direct/e059fc40711046bca69efcfe32868319.png) > > 这个版本和一般的append不一样 > > * **正常append在插入是遇到源数据的'\\0',就会停止插入** > * **这个版本n的优先度大于'\\0',只要没有插入到n个数据,哪怕遇到'\\0'也不会停下** > * **如果源数据的元素个数小于n,这种行为是C++未定义的行为,编译器不回去自动检查你的append有没有发生越界** > > ![](https://i-blog.csdnimg.cn/direct/0dfa7dcd14f549729e738fa894cef803.png) > > 注意看我这段代码,我用append("9",10),明显我的源数据是不够的10位,但是在vs2019下,在插入完9以后,还插入了9个\\0,但是这种行为C++是没有做出定义的,所以在不同的编译器下的结果可能不一样 > > ![](https://i-blog.csdnimg.cn/direct/fcf2f6dfc2e04a20a960c1acd2f3482a.png) > > 这一图片就很好的证明了,该版本对于\\0的优先级并没有n的高 > ![](https://i-blog.csdnimg.cn/direct/3f125a6fa6c044a2aa071cdabc3ab654.png) > > 这个版本是用迭代器来完成插入操作 > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > > > int main() > { > string s("12345"); > string ss = "one two three"; > cout << s<< endl; > s.append(ss.begin(), ss.end()); > cout < > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/de1b4cc6070f496987313d11084f0420.png) ### push_back > ![](https://i-blog.csdnimg.cn/direct/82e7b211cc404c67b9c14fea0a8dfc51.png) > > 对string对象进行尾差一个字符 > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > > > int main() > { > string s("12345"); > s.push_back('a'); > s.push_back('b'); > s.push_back('c'); > s.push_back('d'); > s.push_back('e'); > cout << s << endl; > > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/3428288e22304d51a7f1e59ef538209c.png) ### assign ![](https://i-blog.csdnimg.cn/direct/b98de55d5b174e2eabf5b40451e7d4e5.png) assign的作用: > 是将原string对象中的数据,用参数列表中的第一个参数或者他的一部分覆盖掉原有数据 这个assign的版本参数设置和刚刚我们讲的append一模一样,其实确实一个意思 > ![](https://i-blog.csdnimg.cn/direct/1b94374815674c94846da19db3f70479.png) > > ![](https://i-blog.csdnimg.cn/direct/2f1ce29421084188bc9a6ab5b6fc1c9d.png) > > 这个就是用str字符串和s的数据去覆盖原string的数据 > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > > > int main() > { > string s("12345"); > string ss("ont two three"); > s.assign("abcde"); > cout << s << endl; > s.assign(ss); > cout << s << endl; > > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/e8806eedd242496aa164862bc34fa2a2.png) > ![](https://i-blog.csdnimg.cn/direct/491eb34a6636436dbd9acfccaaae9d84.png) > > ![](https://i-blog.csdnimg.cn/direct/b38c67be93f24833b0d163e8f1dba066.png) > > 这两个的功能比较类似我就放一起说了,一个是用string对象的subpos-\>sublen的数据来覆盖源数据 > > 一个是用迭代器来实现用新数据来覆盖源数据 > > 这里的一些使用性质用参考和append相同的参数列表 > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > > int main() > { > string s("12345"); > string ss("ont two three"); > s.assign(ss, 0, ss.size()); > cout << s << endl; > s.assign(ss.begin()+ 1, ss.end() - 1); > cout << s << endl; > > return 0; > } > ``` > > ![](https://i-blog.csdnimg.cn/direct/ed6df90a3d344d278570034abb94625f.png) > ![](https://i-blog.csdnimg.cn/direct/57a3875c57084fe3a88c68b73de53e72.png) > > 然后这个版本我就不在多说了,参数的使用规则和append那个是一致的,只是这个是覆盖原数据而已 ### insert ![](https://i-blog.csdnimg.cn/direct/e5f74a92d84e42a6b31bb024199cf0e2.png) intsert的作用: > 在**pos位置之前**插入数据 还是一样我们不需要全部掌握所有的版本,只要掌握我框上的版本就可以,其他的用的时候在查也是可以的 > ![](https://i-blog.csdnimg.cn/direct/18cb10f917d14cdbbd1c750f6c35bbbc.png) > > 在pos位置插入一个string对象 > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > int main() > { > string str("ss"); > string s = "xx"; > str.insert(0, s); > cout << str << endl; > > return 0; > } > ``` > > 代码结果: > ![](https://i-blog.csdnimg.cn/direct/d3c0b249de8c47ba8e5a0c4748fce26f.png) > > ![](https://i-blog.csdnimg.cn/direct/368919fc79b94c3584691fe320183370.png) > > 在pos位置之前插入一个字符串,其实这个版本和string版本是一样的(因为C++支持隐式类型转换),没必要再去设计一个char\*的版本 > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > int main() > { > string str("ss"); > str.insert(0, "1234"); > cout << str << endl; > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/2233de5934694bd0893fdfe211065a4a.png) > > ![](https://i-blog.csdnimg.cn/direct/a1a0b6d9cd65445cba5d2fb0d8e4a787.png) > > 在pos位置之前插入n个字符c这也是insert唯一种不使用迭代器可以插入字符的方式 > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > int main() > { > string str("ss"); > str.insert(0, 3,'1'); > cout << str << endl; > > return 0; > } > ``` > > 代码结果: > ![](https://i-blog.csdnimg.cn/direct/c6c274d1ad9343f4b8394cf366c11e13.png) > ![](https://i-blog.csdnimg.cn/direct/2af980df10ab46d994663ecbb9ddbaa5.png) > > 一个是在迭代器p位置之前插入n个字符c > > 另一个是在迭代器p位置之前插入一个字符c > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > int main() > { > string str("ss"); > cout << str << endl; > str.insert(str.begin(), 3, '1'); > cout << str << endl; > str.insert(str.begin(),'2'); > cout << str << endl; > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/27f1593075c44b01bb7987fcdb582f15.png) ### erase ### ![](https://i-blog.csdnimg.cn/direct/fa2af2a9b5ca4d5aa874a5633a198ad2.png) erase的作用: > **删除指定位置的数据,或是指定位置的后len个长度单位的数据区间** > ![](https://i-blog.csdnimg.cn/direct/6a1871edb8fc4d76abe6f9636cc276b0.png) > > len是一个缺省值,如果你没传len的值,那么len就会默认赋nops值 > > ![](https://i-blog.csdnimg.cn/direct/7e6963e6e2af46fa86e0bcea860b4ca4.png) > > 我们可以看到nops其实就是一个静态属性,size_t == unsigned int,所以当nops == -1时,npos就是最大的正整数 > > * 共享性 > * 类直接访问 > * 再类加载是初始化 > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > int main() > { > string str("abce"); > str.erase(0, 1); > cout << str << endl; > str.erase(0); > cout << str << endl; > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/a7879cc4988d473ba3bed3b93127a14b.png) > ![](https://i-blog.csdnimg.cn/direct/f67e24405aba4b58821a9d75b429e872.png) > > 这俩版本分别是 > > * 删除迭代器指向的未知的数据、 > * 删除俩个迭代器之间的数据(左闭右开) > > 左闭右开:包括左迭代器指向的数据,但不包括右迭代器指向的数据(删除到他的前一个位置为止) > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > int main() > { > string str("abce"); > str.erase(str.begin() + 1); > cout << str << endl; > str.erase(str.begin() + 1, str.end() - 1); > cout << str << endl; > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/2131c0ec942441b7b8ca8ac5049ed734.png) ### replace ![](https://i-blog.csdnimg.cn/direct/0ab2be19b0dd4f50bc981fe8672adc21.png) 主要关注我框选的几个版本掌握即可,其他的使用场景不多,可以用到在来看 replace的作用: > **用于替换字符串中部分内容的成员函数,就是将string对象中的数据或者一部分数据替换掉** > > (替换不一定就是等值个数的数据替换,也可能是1个元素被替换成2个元素,2个元素被替换成1个元素) > ![](https://i-blog.csdnimg.cn/direct/a0d818fbc12f49a49a3fa0900030d956.png) > > 在pos位置到pos+len的区间,替换为str(左闭右开) > > 在迭代器i1到迭代器i2区间,替换为str > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > int main() > { > string str("abcdef"); > string s("123456"); > str.replace(0, str.size(), s); > cout << str << endl; > str.replace(str.begin(), str.end(), "aaaa"); > cout << str << endl; > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/626aa5280bc7440f8ef0468908ba49aa.png) > ![](https://i-blog.csdnimg.cn/direct/deaa07e63a6a4f22bafc7551c2ed9ae9.png) > > 这个两版本其实和上面的string版本是一样的,就是替换的源数据不一样,一个是string一个是char\*类型的,这里我就不再去演示了 > ![](https://i-blog.csdnimg.cn/direct/c3880b409f49411b920040b6e34c1241.png) > > 把指定的区间的数据替换成n个字符c > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > int main() > { > string str("abcdef"); > string s("123456"); > str.replace(0,1, 5,'1'); > cout << str << endl; > str.replace(str.begin(), str.begin()+1, 5, 'z'); > cout << str << endl; > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/01d33d8f3d5c479ca8ea971322b44be8.png) > > ------------------------------------------------------------------------------------------------------------------------------ > > 这里在讲一下,当len的长度为0的的时候、当i1和i2相等的时候 > > ```cpp > #define _CRT_SECURE_NO_WARNINGS 1 > #include > using namespace std; > int main() > { > string str("abcdef"); > string s("123456"); > str.replace(0,0, 5,'1'); > cout << str << endl; > str.replace(str.begin(), str.begin(), 5, 'z'); > cout << str << endl; > > return 0; > } > ``` > > 代码结果: > > ![](https://i-blog.csdnimg.cn/direct/d32c8f6a88b64557ac654fed00b6843a.png) > > 我们可以看到,当len的长度为0,i1和i2相等的时候,其实此时的替换操作,就变成了插入操作,而且是pos(i1)位置之前插入的操作,虽然replace可以进行这一的操作,但是如果我们要进行插入操作,还是建议直接使用insert ### swap ![](https://i-blog.csdnimg.cn/direct/bc3bb3de22194af9a0f08a5498442066.png) ![](https://i-blog.csdnimg.cn/direct/3c459e134b2e4cccbc1b033ccf3066bf.png) swap作用: > **交换俩个string对象的数据** 这个第一的个swap是string类的成员函数,第二个是std域名中的swap函数 > 区别: > > * string类中的swap成员函数内部是直接交换两个string对象的地址,来达到数据交换的效果 > * std域名中的swap函数内部是创建一个相同的数据类型作为媒介,来交换两个对象的数据,但是这种交换会涉及到对象的拷贝构造,所以并没有类中的swap来的高效 ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string str("abcdef"); string s("123456"); str.swap(s); cout << str << endl; return 0; } ``` 代码结果: ![](https://i-blog.csdnimg.cn/direct/b83cfe2c0f64429fa4adf59d11c886b4.png) pop_back ![](https://i-blog.csdnimg.cn/direct/b3071c20eebb4474b36a07b09d410ce9.png) pop_back的作用: > **删除字符串的最后一个元素** ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string str("abcdef"); str.pop_back(); cout << str < **返回一个char\*地址** 我们一开始就说了,string其实就是一个字符串,是字符串就会有char\*的地址,只是string对char\*字符串进行了一系列的封装,最后以string 的形式供我们使用 那有没有什么使用场景呢? 有的 ![](https://i-blog.csdnimg.cn/direct/315b160517fe41a4a306289ca1721a14.png) 当我们想用printf来打印string对象的时候,你会发现报错,为什么会这样呢?因为printf会根据%s来判断接受的地址是一个字符串地址,但是string对象他的地址里面不光是存储了字符串,还有字符串的元素个数、最大存储容量等等信息,所以你直接当字符串打印打出来的就很可能是乱码 ![](https://i-blog.csdnimg.cn/direct/ea88897376f644cf983dea1c4a94d6c8.png) 当你使用c_str和data的时候就可以正常打印,这相当于你直接把string对象中的字符串地址提取出来了 ### copy ![](https://i-blog.csdnimg.cn/direct/2006b5e7261d4dec9b3dbfea9e1a8c5b.png) copy作用: > **用数组s来接收string对象距离pos位置的len个距离单位的数据区间** ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string str("abcdef"); char data[20]; str.copy(data, 2, 0); cout << data < **根据给的第一个参数,在string对象中从pos位置开始查找第一次匹配的数据,返回该数据的的下标,找不到返回nops** ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string str("abcdef"); string s("b"); //size_t find (const string& str, size_t pos = 0) const; size_t judge = str.find(s); if (judge != string::npos) { cout << str[judge] << endl; } //size_t find (const char* s, size_t pos = 0) const; judge = str.find("b"); if (judge != string::npos) { cout << str[judge] << endl; } //size_t find (const char* s, size_t pos, size_t n) const; judge = str.find("b", 0, 1); if (judge != string::npos) { cout << str[judge] << endl; } //size_t find (char c, size_t pos = 0) const; judge = str.find('b'); if (judge != string::npos) { cout << str[judge] << endl; } return 0; } ``` 代码结果: ![](https://i-blog.csdnimg.cn/direct/62464712674f4de2842b92df7d08508d.png) 我相信其他几个版本大家都可以看得懂,就是 ``` size_t find (const char\* s, size_t pos, size_t n) const; ``` 这个版本可能有点看不明白,我这里再解释一下 > **从string对象的pos位置开始查找,查找的对象是s字符串数组的前n位** #### rfind和find_first_of、find_last_of、find_first_not_of、find_last_not_of rfind的作用: > **从后往前查找** find_first_of的作用: > **从前往后查找,只要查找到数据中的任意一个元素,就返回下标** find_last_of的作用: > **从后往前查找,只要查找到数据中的任意一个元素,就返回下标** find_first_not_of的作用: > **从前往后查找,除了数据中的元素,查找到其他的任意元素就返回下标** find_last_not_of > **从后往前查找,除了数据中的元素,查找到其他的任意元素就返回下标** 因为他们的参数列表都是一致的,所以我就不一一演示了 ### **substr** ![](https://i-blog.csdnimg.cn/direct/a24e514a177f4d78b0395fce424e0915.png) substr的作用: > **将string对象的pos位置到pos+len位置的数据区间,拷贝出来构成一个子串** ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; int main() { string str("abcdef"); string s = str.substr(0,3); cout << s << endl; return 0; } ``` 代码结果: ![](https://i-blog.csdnimg.cn/direct/49df0beba90344ca9640d06d8f8253b3.png) ## string中iterator迭代器 迭代器 iterator一般是做一个类型来创建对象,或者是作为其他迭代器的返回值或者参数类型 (**你可以理解iterator是一个被重命名的char\***) 迭代器就是string中专门用来访问string对象数据的,为什么明明有了\[\]还需要iterator迭代器呢? 这个也是为了和其他容器保持一致,因为在内存空间连续存储的string和vector容器中确实感觉作用并不突出,但是在内存空间不连续的链表结构和树结构上那就效果十分显著了,我们只需要创建一个迭代器,然后去根据自己的需求是修改还是迭代打印就可以,不需要去关注他们之间的访问细节 ### begin和end ![](https://i-blog.csdnimg.cn/direct/d025afbfc5e946a5b070c0ff75d19ada.png) 这里为了更好的理解,我们先把上面这些迭代器分为两大类 > begin() end() > > cbegin() cend() > > rbegin() rend() > > crbegin() crend() > 我们先来了解一下begin和end ![](https://i-blog.csdnimg.cn/direct/48c4c5a1931d446e9fa09fd45a081735.png) ![](https://i-blog.csdnimg.cn/direct/adb87afb2d2145b0bffa0655dd99381a.png) begin的作用是返回字符串的第一个元素的位置,end的作用是返回字符串的最后一个元素的位置 然后it指向该元素,你可以暂时理解为是一个指针 ```cpp int main() { string s1("hello world"); //要指定类域才可以直接访问,这个it可以自己去设置名字 string::iterator it = s1.begin(); while (it != s1.end()) { cout << *it << " "; ++it; } return 0; } ``` 注意begin()和end()的也有const版本,但是他们的返回值是const_iterator (这一点和iterator的普通版本和const版本是相对应的) 之所以会引用iterator迭代器来遍历,就是因为下标+\[\]只适用于部分容器,底层物理有一定连续的,链式结构、树形结构、哈希结构只能用迭代器,迭代器才是容器访问的主流形态 每一个容器里面都有一个iterator迭代器,但是iterator也分俩个版本![](https://i-blog.csdnimg.cn/direct/9ec97497287c45fcb0251f73ffb73d7a.png) 一个是无const修饰的版本,一个是有const修饰的版本(const修饰的是this地址) **注意const_iterator不是const iterator** |-----------------------|------------| | iterator() | 无const修饰 | | const_iterator()const | 修饰\*it不能修改 | | const iterator() | 修饰it不能修改 | 这里在讲一下const_iterator的使用场景 ```cpp #define _CRT_SECURE_NO_WARNINGS 1 #include using namespace std; void print(const string& str) { //如果这里不使用const_iterator的话,str.begin就会报错 string::const_iterator it = str.begin(); while (it != str.end()) { cout << *it++ << ""; } cout << endl; } int main() { string s("1234567890"); print(s); return 0; } ``` 代码结果: > 1 2 3 4 5 6 7 8 9 0 这里在解释一下为什么会报错,因为C++的编译器在检测到str为const修饰的对象以后,在str去调用begin迭代器就会去调用那个const_iterator begin()const版本的迭代器,这个时候的返回值就是const_iterator类型的,你用iterator类型的it去接收const_iterator的类型,类型就会不匹配 ### 范围for 其实范围for的原理也是迭代器的调用,编译器会去调用迭代器去实现所需要的功能,但是如果想验证这一观点的话,现阶段最好的办法就是,自己去模拟实现一个string类,然后在迭代器的实现部分,begin和end就先不实现,然后去使用范围for看看他是否可以正常运行 ### cbegin/rbegin/crbegin和cend/rend/crend 除了begin/rbegin和end/rend,其他这几个迭代器是C++11中引入的,其实在功能上他们之间没有什么区别 > begin == cbegin end == cend rbegin == crbegin rend == crend 加c的前缀就是const版本,但是begin、end、rbegin、rend都是自带const版本的,所以特意分出来一个const版本意义不是特别大,当然如果你使用了C++11新引入的迭代器,那么在代码的可读性上绝对是有提升的

相关推荐
格林威2 小时前
工业相机图像高速存储(C#版):直接IO(Direct I/O)方法,附Basler相机实战代码!
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机
IOT-Power2 小时前
Qt+C++ 控制软件架构实例
开发语言·c++·qt
顾温2 小时前
c# 多线程
开发语言·c#
草莓熊Lotso2 小时前
Linux 进程间通信之命名管道(FIFO):跨进程通信的实用方案
android·java·linux·运维·服务器·数据库·c++
草莓熊Lotso2 小时前
MySQL 表约束核心指南:从基础约束到外键关联(含实战案例)
android·运维·服务器·数据库·c++·人工智能·mysql
xiaoye-duck2 小时前
《算法题讲解指南:递归,搜索与回溯算法--二叉树中的深搜》--10.二叉搜索树中第k小的元素,11.二叉树的所有路径
c++·算法·深度优先·递归
MegaDataFlowers2 小时前
认识O(NlogN)的排序
java·开发语言·排序算法
小鸡吃米…2 小时前
调试线程应用程序
开发语言·python
m0_672703312 小时前
上机练习第48天
数据结构·c++·算法