目录
- 1. C++标准库与STL
- 2. string是什么
- 3. string的使用
- 3.1 构造与拷贝构造
- 3.2 遍历访问方式
- 3.3 STL中算法操作相关内容
- 3.4 容量相关成员函数
- 3.5 内容修改相关成员函数
- 3.6 string类操作成员函数
- 3.7 string的非成员函数
1. C++标准库与STL
- 编程语言标准库中,有着许多基础且重要的编程资源,相较于C标准库,C++在C的基础上又添加了很多实用且功能强大的功能模块,使得C++进一步在编程效率上得到了长足的进步与发展。
- STL(standard template libaray),即标准模板库,其为C++标准库新增内容中及其重要的组成部分之一,不仅包含了许多重要的算法内容,而且以模板的方式为我们提供了大量的可以直接使用的常用数据结构。
- STL有众多实现版本,各大编译器厂商都有自己实现,这里我们依照学习的STL为PJ版本(Linux操作系统下,g++编译器使用,开源)
- STL由六大部分(组件)组成,算法,仿函数,迭代器,容器,适配器,内存池
2. string是什么
- string为C++标准库中自带的一个类(basic_string模板的实例化),它底层实则为一个字符数组,用来存储字符串,其附带的成员函数实现了对存储其中字符串的各种操作,直接调用成员函数的方式极大的便利了我们的使用与对字符串的高效操作。
- string从实现与函数接口上,与STL中容器部分的设计思路相似,之所以没有划分到STL中,是因为string的出现先于STL。
3. string的使用
3.1 构造与拷贝构造
- 直接构造:(默认构造为空字符串,string重载了流插入操作符)
string s1;
- 字符串传参构造:
string s2("hello world!");
- 字符串指定长度构造:(从字符串头部,向后6个字符)
string s3("hello world!", 6);
- 使用已存在的同类型对象进行拷贝构造:
string s4(s2);
- 用已存在对象的一部分进行拷贝构造:(从下标为6的位置,向后6个字符)
string s5(s2, 6, 6)
- 使用指定个数的字符进行构造:(使用6个字符x构造一个string类)
string s6(6, 'x');
- 使用已存在对象的迭代器进行构造:(可指定范围,迭代器构造规则为,左闭右开)
string s7(s2.begin(), s2.end());
- 默认成员函数:重载的赋值运算符
<1> 使用参数string类的内容进行赋值
<2> 使用参数字符串进行赋值
<3> 使用字符进行赋值
//使用赋值运算符创建string对象,会直接进行拷贝构造
string s8 = s2;
s8 = "hello world!";
s8 = 'a';
3.2 遍历访问方式
1. operator [ ]
补充:成员函数(计算string中字符串的大小,拥有多少个有效字符不计算’\0’)
- size()
- length()
for(int i = 0; i < s1.size(); i++)
{
cout << s1[i];
}
cout << endl;
2. 迭代器(iterator)
- 模板与类都有自带的迭代器,在声明获得迭代器时要指定类域
- 成员函数 begin() 与 end() 会分别返回指向string头部与尾部的迭代器
- string的迭代器类型为string::iterator,名称较长,可用auto自动推演类型
- 迭代器支持
++
,--
,等关于加减操作遍历与调整方式,反向遍历有反向迭代器(r_begin(),r_rend())- string的迭起底层实现可能为指针,其使用指针的前提为底层物理空间上有一定的连续,但并非所有的迭代器都由指针实现
- 迭代器可以分为四种,正向与反向迭代器,被const修饰的正向与反向迭代器
注:const迭代器,为this指针被const修饰,string类的内容不能被改变
string s("hello world!");
//正向迭代器(正向访问)
for(string::iterator it = s.begin();it != s.end(); it++)
{
cout << *it;
}
cout << endl;
//反向迭代器(反向访问)
for(string::reverse_iterator r_it = s.rbegin(); r_it != s.rend(); r_it++)
{
cout << *r_it;
}
cout << endl;
//const修饰正向迭代器
for(string::const_iterator c_it = s.cbegin(); c_it != s.cend(); c_it++)
{
cout << *c_it;
}
cout << endl;
//const修饰的反向迭代器
for(string::const_reverse_iterator cr_it = s.crbeigin(); cr_it != s.crend(); cr_it++)
{
cout << *cr_it;
}
cout << endl;
- 迭代器访问方式的设计理念:
为不同的容器,数据结构提供了统一的遍历方式,对内部实现进行了封装,提高了通用性,使得我们对STL的学习难度与成本大大降低。
3. 范围for
- 底层为正向迭代器访问的替换
for(auto e : s)
{
cout << e;
}
cout << endl;
4. at
- 使用方式与operator[ ]类似,使用[]越界会直接报错,而使用at越界只会抛出异常
for(int i = 0; i < s.zie(); i++)
{
cout << s.at(i);
}
cout << endl;
3.3 STL中算法操作相关内容
- STL中将一些我们经常会使用到的算法相关操作通过重载与模板的方式进行了实现,由此支持不同类型数据结构,容器,与几乎所有使用场景的使用,大大方便我们的使用提高了编程效率。
- 此类方法都在标准库的
algorithm
头文件中包含,且使用前需要开放命名空间std,下面我们来简单看几个。
1. reverse(逆置)
- 支持各种迭代器,会逆置两个迭代器之间范围内的数据,以函数模板的方式实现。
//逆置整个字符串
reverse(s.begin(), s.end());
2. swap(交换)
- 库中交换功能的实现方式为传引用,因此,传参时无需取地址,并且使用了函数模板的方式使得其支持各种数据类型。
//交换首尾两个位置的字符
swap(s[0], s[0] + s.size() - 1);
3.4 容量相关成员函数
- vs环境下,32位系统,string类的空间大小为28字节。它以动态开辟空间的方式实现对字符串的储存,初始时会给予string类16字节的大小,随着需要储存数据的增加不断为1.5倍的速度扩容当前空间。
- vs下对于string的每次扩容不是标准的1.5倍,同时按照一套专属的对齐机制进行空间的对齐。
- Linux操作系统g++编译器中,string类的扩容的机制为,初始空间大小为0,随着数据的增长从1开始不断二倍扩容,g++下的每次扩容都是标准扩容。
1.capacity
- 返回当前string对象的容量大小
s.capacity()
2. max_size
- 返回string对象理论上所能开辟的最大空间,能够承载的最大字符数
- 空间大小常常达不到理论上的最大字符,所以能承载的最大字符数也只是理论上的,不具有参考意义
s.max_size()
3. reserve
- 让string对象开辟出指定大小的空间,当指定数(Byte)大于目前空间的大小时,会进行扩容
- 当指定数小于目前的容量时,不会进行缩容。
//n为指定数
s.reserve(n)
4. resize
- 让string对象开辟出指定大小的空间,当指定数大于容量时,进行扩容,并在额外开辟出的空间中存储指定字符,当未指定字符时,默认存储字符
\0
- 当指定数小于容量但大于有效字符数时,会进行缩容
- 当指定数小于有效字符数时,只会缩容到有效字符大小,不会影响数据。
//默认填充字符'\0'
s.resize(n)
//指定填充字符
s.resize(n, 'a');
3.5 内容修改相关成员函数
1. append
- 使用string类做参数,将参数string类的内容拼接到当前string类后
- 使用string类做参数,将参数stirng类的一部分内容拼接到string类后
- 使用字符串做参数,将参数字符串拼接到当前string类后
- 使用字符串做参数,将参数字符串的一部分拼接到当前string后
- 使用字符做参数,将指定个数的字符拼接到当前string类后
string s2("hello world!");
s.append(s2);
//将s2从下标6开始向后的六个字符拼接到s后
s.append(s2, 6, 6);
s.append("hello world!");
//将字符串的前五个字符拼接到s后
s.append("hello world!", 5);
//将3个字符a拼接到s后
s.append(3, 'a');
2. push_back
- 将指定的字符插入到当前string类后
//将字符a插入到string类后
s.push_back('a');
3. operator+=
- 将指定的参数string类的内容拼接到当前string类后
- 将指定的参数字符串拼接到当前string类后
- 将指定的参数字符拼接到string类后
string s2("hello world!");
s += s2;
s += "hello world!";
s += 'a';
4. insert
- 在当前string类的指定下标处,插入参数string类的内容
- 在当前string类的指定下标处,插入参数string类的一部分内容
- 在当前string类的指定下标处,插入参数字符串
- 在当前string类的指定下标处,插入参数字符串的一部分内容
- 在当前string类的指定下标处,插入指定个数的字符
string s2("hello world!");
s.insert(3, s2);
//将s2从下标为6开始向后六个字符的部分插入到s下标为3的位置上
s.insert(3, s2, 6, 6);
s.insert(3, "hello world!");
//将字符串的前五个字符插入到s下标为3的位置上
s.insert(3, "hello world", 5);
//在s下标为3的位置上插入5个'a'字符
s.insert(3, 5, 'a');
5. erase
- 删除从指定下标位置开始,指定长度的内容
补充1:erase第一个函数模板的缺省参数分别为pos = 0,len = npos
补充2:npos为string类的静态成员变量,其定义为(size_t)-1
,即整形能够表示的最大数
补充3:当指定长度大于当前string类内容的有效长度时,只会将当前string类中的内容全部删除- 删除指定迭代器区间中的当前string内容
- 删除指定迭代器位置的当前string内容
//默认将string类清空
s.erase();
//删除string类从下标2开始往后的三个字符
s.erase(2, 3);
s.erase(s.begin(), s.end());
s.erase(s.begin());
6. assign
- 将参数string类的内容赋值给当前string类
- 将参数string类的一部分赋值给当前string类
- 将参数字符串赋值给当前string类
- 将参数字符串的一部分赋值给当前string类
- 将指定个数的字符赋值给当前string类
string s2("hello world!");
s.assgin(s2);
s.assgin(s2, 6, 6);
s.assgin("hello world!");
s.assgin("hello world!", 5);
s.assgin(5, 'a');
7. replace
- 将参数string类的内容替换到当前string类的指定区间上
- 将参数string类的指定部分内容替换到当前string类的指定区间上
- 将参数字符串的内容替换到当前string类的指定区间上
- 将参数字符串的指定内容替换到当前string类的指定区间上
- 将指定个数的指定字符串,替换到当前string类的指定区间上
string s2("hello world!");
//将s2的内容替换到以下标2开头长度为3的区间处
s.replace(2,3,s2);
//将s2的内容的指定部分替换到以下标2开头长度3的区间处
s.replace(2,3,s2,6,6);
s.replace(2,3, "hello world!");
s.replace(2,3, "hello world!", 6,6);
s.replace(2,3 , 3, 'a');
8. swap
- 交换参数string类与当前string类
- 此swap为string类中自带的成员函数swap,与算法库中的swap实现方式不同
- 算法库中的swap会生成中间变量,然后赋值交换,而string自带的swap会直接交换两个string指向存储空间的指针,效率更高
string s2("hello world!");
s.swap(s2);
9. pop_back
- 删除string类的最后一个字符
string s("hello world!");
s2.pop_back();
3.6 string类操作成员函数
1. c_str
- C中的字符串类型所调用的函数C++中string类不兼容,为了方便这些函数的调用,C++添加了将string类转换为C中字符串类型的成员函数
string s("test.cpp");
FILE* fp = fopen(s.c_str(), "r");
char get;
do
{
get = fgetc(fp);
cout << get;
} while (get != EOF);
2. data
- 作用与c_str相同
s.data();
3. copy
- 将string类中指定区间的内容拷贝给指定参数字符串
string s("hello world!");
char str[7];
s.copy(str, 6, 6);
str[6] = '\0';
4. find 与 rfind
- 正向与逆向寻找指定字符串
- 从指定下标开始寻找参数string类的内容
- 从指定下标开始寻找参数字符串
- 从指定下标开始寻找参数字符串,向后寻找指定个字符数
- 从指定下标开始寻找,寻找参数字符
- 若未找到返回
npos
string s("hello world!");
string s2("hello");
//默认从下标0开始
s.find(s2);
s.find("world!");
//从下标5开始寻找参数字符串,向后寻找3个字符
s.find("wor", 5 ,3);
s.find('l');
string s3("https:///blog.csdn.net//qq_71806743?type=blog");
int pos = s3.find(":");
cout << s3.substr(0, pos) << endl;
int pos2 = s3.find('/', pos + 3);
cout << s3.substr(pos + 3, pos2 - pos - 3) << endl;
cout << s3.substr(pos2 + 1) << endl;
5. find_first_of 与 find_last_of
- 正向与逆向寻找指定参数字符串中字符
- 从指定下标开始寻找参数string类中包含的字符
- 从指定下标开始寻找参数字符串中包含的字符
- 从执行下标开始寻找参数字符
- 返回寻找到的下标
string s("hello world!");
string s2("ld");
//默认从0开始
s.find_first_of(s);
//默认从尾部开始
s.find_last_of(s);
s.find_first_of("ld");
s.find_last_of("ls");
s.find_first_of('l');
s.find_last_of('l');
6. find_first_not_of 与 find_last_not_of
- 正向与逆向寻找不是指定参数字符串中字符
- 从指定下标开始寻找不是参数string类中包含的字符
- 从指定下标开始寻找不是参数字符串中包含的字符
- 从执行下标开始寻找不是参数字符
- 返回寻找到的下标
string s("hello world!");
string s2("ld");
//默认从0开始
s.find_not_first_of(s);
s.find_not_last_of(s);
s.find_not_first_of("ld");
s.find_not_last_of("ld");
s.find_not_first_of('d');
s.find_not_last_of('d');
7. substr
- 返回当前string类中的指定区间的字串
string s("hello world!");
//从下标6开始,向后6个字符
string s2 = s.substr(6, 6);
8. compare
- 按照一定规则,长度,字符顺序比较两个字符串大小
s1.compare(s2);
3.7 string的非成员函数
1. 流插入与流提取
- 可以直接使用string类作为流插入流提取操作符的参数
string s1;
cin >> s1;
cout << s2 << endl;
2. operator+
- 因为成员函数的第一个参数只能是this指针,所以string将operator重载为了友元函数
- string类与string类拼接,返回string类型的临时变量
- string类与字符串拼接
- 字符与string类拼接
string s1("abc");
string s2("def");
cout << s1 + s2 << endl;
cout << s1 + "def" << endl;
cout << s1 + 'g' << endl;