=========================================================================
相关代码gitee自取:
C语言学习日记: 加油努力 (gitee.com)
=========================================================================
接上期:
【C++初阶】八、初识模板(泛型编程、函数模板、类模板)-CSDN博客
=========================================================================
目录
一 . STL简介
什么是STL
STL的版本
HP 原始版本:
P.J. 版本:
RW 版本:
SGI 版本:
STL的六大组件
图示:
二 . string类
C语言中的字符串
C++标准库中的string类
string类(了解)
总结:
string类的常用接口说明(重点)
string类对象的常见构造函数:
图示 -- 第1个构造函数:
图示 -- 第2、3个构造函数:
图示 -- 第4、5个构造函数:
图示 -- 第6、7个构造函数:
补充 -- operator= :(赋值“=”运算符重载函数)
string类对象的容量操作:
使用注意事项:
string类对象的访问即遍历操作:
operator[] :(下标运算符"[ ]"重载函数)
begin + end :(通过迭代器进行遍历)
rbegin + rend :(通过迭代器进行反向遍历)
范围for循环 -- 完成字符串遍历:
本篇博客相关代码:
Test.cpp文件 -- C++文件:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
一 . STL简介
什么是STL
STL(standard template libaray -- 标准模板库):
是 C++标准库 的重要组成部分,不仅是一个可复用的组件库,
而且是一个包罗数据结构和算法的软件框架
STL的版本
HP 原始版本:
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,
本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码
无需付费,唯一的条件就是也需要像原始版本一样做开源使用。
HP版本的STL -- 所有STL实现版本的始祖
---------------------------------------------------------------------------------------------
P.J. 版本:
由 P. J. Plauger 开发,继承自HP版本,
被 Windows Visual C++(VS系列)采用,不能公开或修改。
缺陷:可读性比较低,符号命名比较怪异
---------------------------------------------------------------------------------------------
RW 版本:
由 Rouge Wage公司 开发,继承自HP版本,被 C++ Builder 采用,
不能公开或修改,可读性一般
---------------------------------------------------------------------------------------------
SGI 版本:
由 Silicon Graphics Computer Systems,inc公司开发,继承自HP版本。
被 GCC(Linux)采用,可移植性好,可公开、可修改甚至贩卖,
从命名风格和编程风格上看,阅读性非常高
STL的六大组件
STL的六大组件分别为:
仿函数 、算法 、迭代器 、空间配置器 、容器 、配接器图示:
其中空间配置器也叫内存池,容器可以简单理解成数据结构,配接器也可叫适配器,
容器(数据结构)存储数据需要大量的空间,
如果频繁向堆申请大量的内存空间,效率会有点低,
所以STL中有了空间配置器(内存池),专门给容器(数据结构)提供内存空间,
内存空间的初始化工作则交给定位new完成
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
二 . string类
C语言中的字符串
C语言中,字符串是以 ‘\0’ 结尾的一些字符的集合,为了操作方便,
C语言标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,
不太符合OOP(面向对象程序设计)的思想,而且底层空间需要用户自己管理,
稍不留神可能还会越界访问
C++标准库中的string类
string类(了解)
string类严格来说不是STL容器,但从归类的角度来说,
其实可以把字符串看成是数据结构中的串,一个专门管理字符的串。
因为string产生得比STL早,所以在C++文档中被纳入到了标准库中,而不是STL中
string字符串是表示字符序列的类,虽然我们通常叫做string类,
但string其实是一个模板类的一个实例
标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,
但添加了专门用于操作单字节字符字符串的设计特性
string类使用char作为其字符类型,使用它的默认char_traits和分配器类型
(关于模板的更多学习,可参阅:C++ string类模板)
string类是basic_string模板类的一个示例,它使用char来实例化basic_string模板类,
并用 char_traits 和 allocator 作为 basic_string 的默认参数
(关于模板的更多学习,可参阅:C++ string类模板)
注意:
string类独立于所使用的编码来处理字节,
如果用来处理多字节或变长字符(如UTF-8)的序列,
string类的所有成员(如长度或大小)以及它的迭代器,
将仍然按照字节而不是实际编码的字符来操作
总结:
string是表示字符串的字符串类
该类的接口和常规容器的接口基本相同,再添加了一些用来操作string的常规操作
string类在底层实际是 basic_string模板类 的别名(引用),
使用了 typedef basic_string<char, char_traits, allocator> string; 重命名为了string
不能操作多字节或者变长字符的序列
在使用string类时,必须包含 #include<string>头文件 以及 using namespace std;
(也可以不用完全展开,展开string对应的那部分即可)
---------------------------------------------------------------------------------------------
string类的常用接口说明(重点)
string类对象的常见构造函数:
(constructor)构造函数名称 对应功能说明 string(); (重点) 创建一个字符串对象,
但没有使用字符串初始化string (const char* s); (重点) 创建一个字符串对象,
并使用常量字符串初始化string (const string& str); (重点) 创建一个字符串对象,
并使用另一个字符串对象
进行初始化string (const string& str, size_t pos, size_t len = npos); 拷贝str字符串中pos下标
开始的len个字符,
拷贝给创建的字符串对象string (const char* s, size_t n); 拷贝一个常量字符串的
n个长度的字符,
拷贝给创建的字符串对象string (size_t n, char c); 填充n个c字符到
创建的字符串对象中template <class InputIterator> string (InputIterator first, InputIterator last); 涉及迭代器,
不懂,以后再说图示 -- 第1个构造函数:
图示 -- 第2、3个构造函数:
图示 -- 第4、5个构造函数:
图示 -- 第6、7个构造函数:
补充 -- operator= :
string类对象还可以通过赋值“=”运算符重载函数(operator=)实现初始化
(赋值“=”运算符重载函数)
operator= 有三种实现:
第1种: string& operator= (const string& str); -- string字符串类对象赋值
第2种: string& operator= (const char* s); -- 直接写出字符串进行赋值
第3种: string& operator= (char c); -- 单个字符进行赋值对应图示:
---------------------------------------------------------------------------------------------
string类对象的容量操作:
函数名称 功能说明 size (重点) 返回字符串有效字符大小(长度) length 返回字符串有效字符长度 capacity 实际能够存储的有效字符个数 empty (重点) 检测字符串是否为空串,是返回true,否则返回false clear (重点) 清空字符串有效字符 reserve (重点) 为字符串预留空间,可以进行扩容操作
(只影响容量,不会影响数据)resize (重点) 将有效字符的个数分割成n个,多出的空间可用字符c填充
(即影响容量,也影响数据)使用注意事项:
size() 与 length() 方法底层实现原理完全相同,
引入 size() 的原因是为了与其它容器的接口保持一致,
一般情况下基本都是用 size()
对应图示: clear() 方法只是将string字符串中的有效字符清空,
不改变底层空间大小
resize(size_t n) 与 resize(size_t n, char c) 都是将字符串中有效字符个数改变到n个,
不同的是当字符个数增多时:
resize(size_t n) 用0来填充多出的元素空间,
resize(size_t n, char c) 用字符c来填充多出的元素空间。
注意:
resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,
如果是将元素个数减少,底层空间总大小不变图示 -- resize :
reserve(size_t res_arg=0) :
为string预留空间,不改变有效元素个数,
当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小图示 -- reserve :
图示 -- reserve增容 :
---------------------------------------------------------------------------------------------
string类对象的访问即遍历操作:
函数名称 功能说明 operator[] (重点) 返回pos下标位置的字符,const string类对象也有对应const调用 begin + end begin:获取第一个字符的迭代器
end:获取最后一个字符下一个位置的迭代器rbegin + rend being + end 是正向迭代器(正向遍历),分为const和非const版本,
rbegin + rend 是反向迭代器(反向遍历),也分为const和非const版本范围for循环 C++11支持更简洁的范围for循环的新遍历方式 operator[] :
string类的 "operator[]" 有两个实现:
(下标运算符"[ ]"重载函数)
第1种: char& operator[] (size_t pos);
第2种: const char& operator[] (size_t pos) const;
string类实现了 "operator[]" 后,
string类对象就可以像数组通过下标一样访问字符串中的字符了图示 -- 第1种实现:
begin + end :
迭代器iterator定义在类域中,但它不是内部类,是一个类型,
(通过迭代器进行遍历)
现在刚接触迭代器的情况下,可以认为迭代器的用法和指针类似
迭代器的 begin() 和 end() 方法,形成的区间是“左闭右开”的,
对于string类对象,begin() 可以理解成指向字符串首字符的指针,
end() 可以理解成指向最后一个有效字符的下一位字符的指针图示 -- 使用迭代器遍历字符串:
图示 -- 迭代器实现operator[]的第2种实现:
rbegin + rend :
和 begin+end 类似,只不过 rbegin+rend 是反向遍历
(通过迭代器进行反向遍历)
begin+end 和 rbegin+rend 各自都有两种实现:
非const版本 和 const版本图示 -- 使用迭代器反向遍历字符串:
范围for循环 -- 完成字符串遍历:
范围for循环底层是通过迭代器实现的,
是C++11支持的更简洁的范围for循环的新遍历方式对应图示:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
本篇博客相关代码:
Test.cpp文件 -- C++文件:
#define _CRT_SECURE_NO_WARNINGS 1//包含IO流头文件:#include<iostream>//完全展开std命名空间:using namespace std;//包含string类头文件:#include<string>#include<vector>#include<list>//template<class T>//T* func(int n)//{//return new T[n];//}//主函数://int main()//{//func<double> (10);////return 0;//}string类的默认成员函数:主函数://int main()//{////string:///*//* string是一个类模板,//* typedef basic_string<char> string;//* 原名是叫 basic_string<char> 的类模板,//* 被typedef重命名为string//*///////string类的默认成员函数 -- 构造函数:////string类中有7种构造函数:///*//*(最常用)1:无参的string -- string(); //*(最常用)2:string (const char* s);//* 3:string (const string& str);//* 4:string (const string& str, size_t pos, size_t len = npos);//* 5:string (const char* s, size_t n);//* 6:string (size_t n, char c);//* 7:template <class InputIterator> string (InputIterator first, InputIterator last);//*///////(最常用)1:无参的string -- string(); //string s1;////////(最常用)2:string (const char* s);//string s2("hello world");///*//* s:接收一个字符串//* 创建一个字符串类,//* 并使用C语言的常量字符串的地址进行初始化,//* s2就存储着这段常量字符串//*/////////赋值“=”,调用拷贝构造初始化://string s3 = s2;///*//* 该写法相当于4中的:string s3(s2);//*/////////3:string (const string& str);//string s4(s3);///*//* str:接收string字符串类对象//* 拷贝str字符串对象,//* 该写法相当于3中的:string s4 = s3;//* 这种方法也是调用拷贝构造函数进行初始化的//*//////*//* 因为string类重载了 流插入/流提取://*operator << / operator >>//* 所以是可以直接使用 输出流/输入流 的//*///cout << "构造函数1:" << s1 << endl;//cout << "构造函数2:" << s2 << endl;//cout << "赋值“=”:" << s3 << endl;//cout << "构造函数3:" << s4 << endl;////////4:string (const string& str, size_t pos, size_t len = npos);///*//* str:接收string字符串类对象//* pos:下标//* len:长度//* npos:一个整型静态const无符号的变量,值为-1,//*因为无符号,-1会是整型的最大值,//*所以如果不对len初始化的话,len的缺省值就是很大的值,//*所以会拷贝很长的字符串,即拷贝pos后的所有字符了//*(len不给默认拷贝str中pos下标后的所有字符)//* //* 这个string类函数的功能是拷贝字符串str的一部分,//* 从pos下标开始,拷贝len长度的字符串//*///string s5(s2, 1, 5);///*//* 从s2字符串的第1个字符开始,//* (s2:“hello world”)//* 往后拷贝5个字符长度的字符串,//* 拷贝结果存放在字符串s5中。//* (s5:“ello ”)//* 注:空格也算一个字符;//* len如果超过str的长度,则str结尾为止//*///cout << "构造函数4:" << s5 << endl;////////5:string (const char* s, size_t n);///*//* s:字符串指针//* n:在s字符串中拷贝字符长度//*///string s6("hello world", 5);///*//* 拷贝“hello world”中的前5个字符,//* 拷贝到s6中初始化s6//*///cout << "构造函数5:" << s6 << endl;////////6:string (size_t n, char c);///*//* n:填充的字符个数//* c:要填充的字符//* 使用n个c字符来填充字符串字符串//*///string s7(10, 'x');///*//* 使用10个‘x’来填充s7//*///cout << "构造函数6:" << s7 << endl;////////7:template <class InputIterator> string (InputIterator first, InputIterator last);///*//* 这个string类构造函数因为涉及迭代器的内容,//* 所以等了解了迭代器再来了解该类构造函数//*///////////////string类的默认成员函数 -- 析构函数:///*//* string类的析构函数://* string字符串类为了支持扩容,//* 其字符数组是动态开辟的,//* 动态开辟的空间使用后要进行释放,//* 其释放工作就是由析构函数负责的,//* 而析构函数一般是自动调用的//*/////////string类的默认成员函数 -- 赋值“=”运算符重载函数:////string::operator= (string类赋值“=”运算符重载函数)///*//* 第1种: string& operator= (const string& str);//* 第2种: string& operator= (const char* s);//* 第3种: string& operator= (char c);//* //* 第1种是支持string字符串类对象进行赋值;//* 第2种是支持字符串(直接写出字符串)进行赋值;//* 第3种是支持单个字符进行赋值//*/////string类第1种赋值方法://s1 = s2; //string字符串类对象进行赋值//cout << "赋值=运算符重载函数1:" << s1 << endl;//////string类第2种赋值方法://s1 = "world"; //字符串(直接写出字符串)进行赋值//cout << "赋值=运算符重载函数2:" << s1 << endl;//////string类第3种赋值方法://s1 = 'x'; //单个字符进行赋值//cout << "赋值=运算符重载函数3:" << s1 << endl;//////return 0;//}namespace ggdpz//防止和std中的string命名冲突:{//string类(自己的):class string{private: //私有成员变量:char* _str; //字符数组(字符串)指针size_t _size; //字符数组大小(长度)size_t _capacity; //字符数组容量/** 可以简单想象string类的私有成员变量* 就是这几个*/};}//string类的遍历和访问:int main(){string s1("hello world");/** string本质是个字符数组,* 只不过通过类封装在一起,* 如果想要遍历string字符串的话有两种方法:* * 遍历的第1种方法:下标 + []* 我们访问数组的时候会使用到方括号“[]”,* string的底层是数组实现的,所以会对“[]”进行重载,* 即operator[],使用string类的operator[]后,* 就可以像访问数组一样访问字符串(字符数组)了*///string类的成员函数 -- 下标“[]”运算符重载函数://string::operator[]/** 第1种: char& operator[] (size_t pos);* 第2种: const char& operator[] (size_t pos) const;* * pos:访问string字符串对象pos下标的* char&:访问pos下标字符后返回该字符的引用(“别名”),* 如果是普通对象则可以修改该字符* * 第2种是第1种的重载版本*///要获取string类对象的长度有两种方法://第一种方法:size()cout << s1.size() << endl; //第二种方法:length()cout << s1.length() << endl; /** size() 和 length() 都是返回字符串对象s1的长度,* 至于同个功能取两个名字,是因为历史发展的关系,* 对于字符串,长度其实使用length()会更合理,* 但由于string产生得比STL早,STL出来前string只有length(),* 当STL出来后,对像set(树)这种数据结构length(长度)就不太合适了,* STL设置接口的时候又需要一定的统一性,length又不能统一使用,* 所以又设置了size(大小),所以string类中又有了size(),* 之后计算string类时使用size()即可,按照STL的标准来* * size() 和 length() 计算string类时不会计算"\0"*///第一种遍历方法:使用for循环遍历字符串:for (size_t i = 0; i < s1.size(); i++)//s1.size() 就可以返回string类对象s1的长度{/** 第1种: char& operator[] (size_t pos);*///遍历打印string类对象s1的字符:cout << s1[i] << " ";//cout << s1.operator[](i) << " "; //等于上面的代码/** 这里遍历string类时使用了下标运算符"[]",* 让string类可以像遍历数组一样被遍历,* 实际string字符串类对象s1调用了下标“[]”运算符重载函数,* s1[i] 即 s1.operator[](i)*/}cout << endl; //换行/** 使用下标符[]可以读数据,* 还可以用它来写数据,跟数组类似,* 因为operator[]调用后返回的是char&,* 是一个引用,所以字符串使用[]可以直接写(修改)数据*/s1[0] = 'x';cout << s1 << endl;//逆置string字符串:int begin = 0; //字符串左边界(下标)int end = s1.size() - 1; //字符串右边界(下标)/** 右边界即字符串最后一个字符下标,* 使用size()获得字符串长度,* 字符串长度 - 1,即最后一个字符下标*/while (begin < end) /** begin < end,* 说明字符串中还有字符能够逆置*/{//1、通过创建临时变量实现两值交换:/** //创建临时变量存储左边界:* char tmp = s1[begin];** //将右边界字符赋给左边界字符:* s1[begin] = s1[end];** //将左边界字符赋给右边界字符:* s1[end] = tmp;*///2、通过C++自带的swap交换函数实现两值交换:swap(s1[begin], s1[end]);/** C++自带交换函数swap,* 因为C++中有了函数模板,* 所以不用考虑实际类型的问题,* 从而实现了通用的swap交换函数,* 使用swap函数需要包含<utility>头文件,* 但是这里该头文件间接包含了,* 所以不用再显式写出来*///进行迭代:++begin; //调整左边界--end; //调整右边界}cout << s1 << endl;//第二种遍历方法:使用迭代器iterator遍历字符串:string::iterator it = s1.begin();/** iterator定义在类域中,但它不是内部类,是一个类型,* 现在还不熟悉迭代器,它的用法类似指针,* 但迭代器不一定是指针* * it可以理解成指向字符串(字符数组)首字符的指针,* begin() 和 end() 迭代器区间是“左闭右开”的,* begin()可以理解成指向字符串首字符的指针,* end()可以理解成指向最后一个有效字符的下一位字符('/0')的指针,*/while (it != s1.end()){*it += 1; //通过迭代器也可以写(修改)数据/** it一开始指向字符串首字符,* *it解引用指针后就可以修改该位置的字符了,*/cout << *it << " "; //打印当前it指针的字符++it; //调整it指针位置/** it实际可能是指针,也可能不是指针,* 可以发现iterator迭代器实际运作方式,* 就像是用指针的方式进行字符串遍历访问和修改*//** 下标运算符[],只能底层有一定连续的情况下使用,* 所以不是所有容器都能够支持。* 真正访问容器最方便主流的就是迭代器* (链式结构、树形、哈希结构 只能使用迭代器)* * 而且各类容器调用迭代器的方式都是相同,* 会调用一个容器的迭代器,* 其它容器的迭代器也就会使用了*/}cout << endl;//对于字符串的逆置,在C++算法中也有,//直接调用即可:reverse(s1.begin(), s1.end());/** 该算法名叫reverse,* 使用时传 开始 和 结束 的位置即可,* 无论什么容器,只要传迭代器区间给reverse,* 就可以实现逆置* * 所以迭代器还可以配合算法使用(nb)* C++算法也是泛型编程,* 不是针对某个容器的迭代器实现的,* 函数模板,针对各个容器的迭代器实现*/cout << s1 << endl;return 0;}//迭代器:int main(){//vector类:vector<int> v;//尾插入数据:v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//vector可以使用类遍历,//也可以使用迭代器遍历:vector<int>::iterator vit = v.begin();/** begin() 会获取第一个位置的迭代器(左闭),* 迭代器的区间都是左闭右开的,* end() 会获取最后一个数据的下一个位置(右开)*/while (vit != v.end())//begin() 还未到 end() :{//解引用vit获取当前位置数据:cout << *vit << " ";++vit; //调整指针}cout << endl; //换行//reverse 也可以实现vector的逆置:reverse(v.begin(), v.end());//list类:list<double> lt;//尾插入数据:lt.push_back(1.1);lt.push_back(2.1);lt.push_back(3.1);lt.push_back(4.1);//使用迭代器遍历list:list<double>::iterator lit = lt.begin();while (lit != lt.end())//begin() 还未到 end() :{//解引用vit获取当前位置数据:cout << *lit << " ";++lit; //调整指针}cout << endl; //换行//reverse 也可以实现list的逆置:reverse(lt.begin(), lt.end());return 0;}int main(){/** 第2种: const char& operator[] (size_t pos) const;* 这个重载构造函数的隐藏this指针是用const修饰的,* 该函数主要是为了解决参数匹配的问题*/string s1{ "hello world" }; //非const对象const string s2{ "hello world" }; //const对象s1[0] = 'x'; //非const对象s1调用"[]"s2[1] = 'x'; //const对象s2调用"[]"/** 非const对象s1调用的"[]"运算符重载函数:* 第1种: char& operator[] (size_t pos);* * const对象s2调用的"[]"运算符重载函数:* 第2种: const char& operator[] (size_t pos) const;* (const:只读,不能修改)* * s1也可以调用“第2种”,非const调用const,* “可读可写” 变成 “只读”,访问权限缩小是允许的,* 虽然都可以调用“第2种”,但是“第2种”的返回值是const char&,* 非const对象调用后返回“只读”的引用(别名)就不合适,* 所以还需要实现“第1种” *//** 对于const的string对象s2,因为“只读”,* 所以不能像s1非const对象那样使用迭代器:*///string::iterator it = s2.begin(); //编译错误//应该是const_iterator(“只读”)而不是iterator(“可读可写”):string::const_iterator it = s2.begin();while (it != s2.end()){//*it += 1; //const对象数据无法被修改cout << *it << " ";++it; //迭代器it本身是可以修改的}cout << endl;/** string类的迭代器中的 begin / end,* 其实现时也有两个版本:* 第1种:iterator begin();* 第2种:const_iterator begin() const;* * 其中“第2种”const版本中,* 它是名字为:const_iterator,* 而不是在iterator前直接加const修饰,* * * * const_iterator it 和 const iterator it:* * const_iterator it 本质是修饰迭代器指向的数据,* 即 *it 不能被修改* * const iterator it 修饰的是迭代器本身,* 迭代器本身不能被修改,即 it 不能被修改,* 要进行遍历的话,得调整it(it++),* 所以不能直接在iterator前加const进行修饰*///容器都能支持范围for循环:for (auto e : s1)//依次取容器对象s1放入e中进行循环遍历:{cout << e << " ";}cout << endl;return 0;}一般在传参才会用到const对象:(让对象在函数中使用时不会被改变)//void func(const string& s)//{////第二种:const对象版本://string::const_reverse_iterator it = s.rbegin();////这里反向迭代器的const版本的类型很长,所以可以用auto进行省略://auto it = s.rbegin();//////进行反向遍历://while (it != s.rend())//{////"只读"////*it1 = 'x'; //报错,无法‘写’//////打印当前字符串s1中it指针指向的字符://cout << *it << " ";////调整it指针://++it;//}//cout << endl; //换行//}//////int main()//{////string类对象://string s1("hello world");///*//* string类对象的三种遍历方式://* 1、下标 + [] :只适用于底层连续的容器//* 2、迭代器 :yyds,能配合算法使用//* 3、范围for :看似好用,实际是依靠迭代器实现的//*//////*//* string的反向迭代器:std::string::rbegin//* //* 第一种:reverse_iterator rbegin();//* 第二种:const_reverse_iterator rbegin() const;//* (rend 和 rbegin 类似)//* //* 对于没有下标的情况,如链表,如果要进行倒着遍历,//* 就可以使用其反向迭代器,掌握了迭代器,//* 就掌握了所有容器的遍历、访问、修改//*/// ////第一种:非const版本://string::reverse_iterator it1 = s1.rbegin();////进行反向遍历://while (it1 != s1.rend())//{////"可读可写"////*it1 = 'x'; //‘写’//////打印当前字符串s1中it指针指向的字符://cout << *it1 << " ";////调整it指针://++it1;//}//cout << endl; //换行/////*//* 打印:"d l r o w o l l e h",//* 即实现了s1:"h e l l o w o r l d"的反向遍历//*/////////第二种:const对象版本://func(s1);///*//* func函数接收const对象,//* s1不是const对象,传过去后权限缩小,//* “可读可写” 变成 “只读”//*/////return 0;//}//int main()//{////std::string::max_size////(返回字符串可以达到的最大长度)//////无参字符串对象://string s1;//////有参字符串对象://string s2("hello world");////cout << s1.max_size() << endl;//cout << s2.max_size() << endl;///*//* 无论是s1的无参,还是s2的有参,//* 打印两者的“max_size()”时,//* (32位系统)都是“214783647”(2^31),//* 即整型最大值的一半,//* 也就是说此时这两个字符串能达到的最大长度为2^31,//* 这是在VS2022上,不同编译器可能不同,//* 不同系统也不同(x64/x86),//* 实际也不一定就开了2^31个字符的空间//*///////////std::string::reserve///*//* (为字符串预留空间,可以进行扩容操作)//* 注意和revserve进行区分,//* reserve:保留//* reverse:反转、逆置//*///s1.reserve(s1.max_size());////保留max_size个空间//////std::string::capacity////(实际能够存储的有效字符个数)//cout << s1.capacity() << endl; //“15”//cout << s2.capacity() << endl; //“15”///*//* string s1;//* string s2("hello world");//*//* s1 和 s2 的capacity打印时都是“15”,//* 即实际可存储的有效字符个数为15,//* 但实际是有16个空间的,有一个空间给了"\0",//* "\0"不算有效字符,而是标识字符,//* 所以capacity打印时为“15”//*///////////std::string::resize////(将有效字符的个数分割成n个,多出的空间用字符c填充)///*//* reserve:只影响容量,不会影响数据;//* resize:即影响容量,也影响数据//* //* resize有两种实现://* 1、void resize(size_t n);//* 2、void resize(size_t n, char c);//* //* resize的使用分三种情况://*/////string s1("hello world");////起始字符串大小(长度)-- 11//cout << s1.size() << endl;////起始容量 -- 15//cout << s1.capacity() << endl; //////第一种情况:resize分割数 > capacity////(即影响容量,又影响数据)////size为11,capacity为15,分割数为100://s1.resize(100); //////分割后字符串大小 -- 100//cout << s1.size() << endl; ////分割后容量 -- 111//cout << s1.capacity() << endl; ///*//* 当 resize分割数 > capacity 时,//* 分割后capacity容量会增容到至少比分割数大,//*(这里 capacity--15 就变成至少比 分割数--100 大的111)//* //* 而字符串数据 size 从15变成了100,//* 那其余“85”的数据是什么数据呢?//* //* 这里的resize是第一种实现:void resize(size_t n);//* 使用resize这种实现,且 resize分割数 > capacity 时,//* 多出的数据就会用 空字符(初识字符)--'/0' 插入,//* 这里的'/0'就不是标识字符了,而是有效字符了,//* '/0'是哪种字符,取决于'/0'是否在size范围中,//* 这里增容后,size从15扩大到100并用'/0'插入充当多余数据,//* 此时“数据'/0'”就不是结束符而是有效数据了//* //* 如果是第二种实现:void resize(size_t n, char c);//* 相同情况下,多出的数据就会用传过来的 字符c 插入//* //* resize分割数 > capacity ---- “扩容 + 尾插”//*///////string s2("hello world");////起始字符串大小(长度)-- 11//cout << s2.size() << endl;////起始容量 -- 15//cout << s2.capacity() << endl;//////第二种情况:size < n < capacity////(只会改变字符串大小size)////size为11,capacity为15,分割数n为12://s2.resize(12);//////分割后字符串大小 -- 12//cout << s2.size() << endl;////分割后容量 -- 15//cout << s2.capacity() << endl;///*//* 在VS中,reserve不会缩容,resize也不会缩容,//* 所以容量还是15,不会改变,//* 字符串数据size还是和第一种情况类似,//* size < 分割数n,数据size就增加到b,//* 并用'/0'或'字符c'插入充当多余数据,//*(插入的数据取决于resize是哪种实现)//* //*(g++ 和 VS 的resize在这种情况下是一样的)//* size < n < capacity ---- “尾插(容量足够)”//*///////string s3("hello world");////起始字符串大小(长度)-- 11//cout << s3.size() << endl;////起始容量 -- 15//cout << s3.capacity() << endl;//////第三种情况:分割数n < size////(只会改变字符串大小size)////size为11,capacity为15,分割数n为5://s3.resize(5);//////分割后字符串大小 -- 5//cout << s3.size() << endl;////分割后容量 -- 15//cout << s3.capacity() << endl;///*//* 和第二种情况一样,//* 因为capacity容量足够,//* 所以不会改变空间大小,//* 只对数据进行分割//*(g++中和VS也是一样的)//* //* 分割数n < size ---- “删除数据,保留分割数n个”//*//////*//* 总结://* resize一定会对数据大小size进行操作,//* 对容量capacity可能会进行增容操作//*(g++ 和 VS 两个主流平台中)//* 在第一种情况中可以 增加数据,//* 在第三种情况中可以 删除数据//* 所以resize的作用就是://* 1、插入数据(如果空间不够还会扩容)//* 2、删除数据//* 更多场景下是用于开空间并初始化//*///}//int main()//{////reserve 和 capacity 的使用://////无参字符串对象://string s1;////有参字符串对象://string s2("hello world");//////使用reserve进行空间预留://s1.reserve(500);///*//* 如果我们知道大概需要多少空间,//* 则可以使用reserve提前开好空间,//* 不需要进行后面的扩容操作,//* 打印结果:"15、511"//* 容量直接一次性扩容到足够存储500个有效字符//* (实际512个空间)//*///////通过capacity检车string的扩容机制://size_t old = s1.capacity(); //s1此时容量//cout << old << endl; ////for (size_t i = 0; i < 100; i++)//{////循环依次,尾插一个字符://s1.push_back('x');////if (old != s1.capacity())///*//* 如果 old 和当前容量不同,//* 说明old容量被扩容了//*///{////打印扩容后容量://cout << s1.capacity() << endl;////更新old容量://old = s1.capacity();//}//}///*//* 如果没有使用reserve进行空间预留,则会进行扩容操作://* //* 打印结果:“15、31、47、70、105”//* 容量从15变成31在变成47……//* 从整体来说,是按当前容量的1.5倍进行扩容//* //* 不同编译器容量和扩容操作不同://* VS -- "15、31、47、70、105" -- 1.5倍扩容//* gcc -- "0、1、2、4、8、16……" -- 2倍扩容//*//////*//* reserve在VS上只会增容;//* //* 在g++上除了增容,还可以缩容,//* 但缩容不会影响数据,只会影响空间,//* 即最多缩容到现存数据大小的空间//*(缩容不会删除数据,最小缩到size)//*///}int main(){return 0;}