🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。
🚁 个人主页:不 良
🔥 系列专栏:🛸C++ 🛹Linux
📕 学习格言:博观而约取,厚积而薄发
🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与诸君一同成长! 🌹
STL简介
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
STL的版本
- 原始版本
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本–所有STL实现版本的始祖。
- P. J. 版本
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。
- RW版本
由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
- SGI版本
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。我们后面学习STL要阅读部分源代码,主要参考的就是这个版本
STL的六大组件
STL提供六大组件,它们之间可以相互组合使用。
1、容器(containers)
容器用来存放数据,包括各种数据结构,如string,vector,list,deque,set,map等。从实现的角度来看,STL容器是一种类模板(class template)。
2、算法(algorithms)
算法包括各种常用的sort,search,copy,erase, find等。从实现的角度来看,STL算法是一种函数模板(function template)。
3、迭代器(iterators)
迭代器作为“泛型指针”,扮演容器和算法之间的粘合剂,用来连接容器和算法。从实现角度来看,迭代器是一种将operator*,operator->,operator++,operator–等指针相关操作进行重载的类模板(class template)。所有的STL容器都带有自己专属的迭代器。原生指针也是一种迭代器。
所谓的原生指针就是我们定义的最普通的指针,形如 类型名 *指针名,类型名可以是基础类型int,double等,也可以是一个类。
当一个类将*和->操作符进行重载时,虽然也可以进行类似指针的操作,但是它已经不是原生指针。
4、仿函数(functors)
仿函数是让一个类看起来像一个函数。其实就是一种重载了operator()的类(class)或者类模板(class template)。
5、配接器(adapters)
一种用来修饰容器,仿函数或者迭代器的接口的东西。配接器修改类的接口,使原来不相互匹配的两个类可以相互匹配,进行合作。
6、空间配置器(allocators)
配置器主要负责空间的配置和管理。从实现角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的类模板(class template)。
内存池:当我们需要频繁的申请和释放空间的时候,要提高效率可以开一个内存池,不用每次都向操作系统中申请内存。内存池的内存也是从堆上申请来的。
STL的缺陷
- STL 库的更新太慢,上一版靠谱的是 C++98,中间的 C++03 基本一些修订。C++11 出来已经相隔了 13 年,STL才进一步更新。
- STL 现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。
- STL 极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。
- STL 的使用会有代码膨胀的问题,比如使用 vector/vector/vector 这样会生成多份代码,当然这是模板语法本身导致的。
标准库中的string类
string类介绍
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
C语言中字符串数组不方便修改,所以C++提供了一个string类。由于历史原因string类比STL产生的早一些,所以string类中提供的函数比较冗余。
- string 是表示字符串的字符串类。
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作 string 的常规操作。
- string类是被typedef出来的,string 在底层实际是basic_string 模板类的别名:
typedef basic_string<char, char_traits, allocator> string;
。 - string类不能操作多字节或者变长字符的序列。
在头文件<string>
中包含多个类模板(Class templates)(basic_string、char_traits
)和类实例化(Class instantiations)(string、u16string、wstring、u32string
):
string管理的是char(1个字节)的数组;wstring管理的是wchar_t(2个字节)的数组;u16string管理的是char16_t(2个字节)的数组;u32string管理的是char32_t(4个字节)的数组。为什么会有这样的差异?因为现实世界中有管理不同字符数组的需求的。
文档中的一些主要模块如下图,我们平时学习STL时可以使用这个网站:Cplusplus
string类对象的常见构造
string类实现了多个构造函数的重载,常用的构造函数如下:
(constructor)函数名称 | 功能说明 |
---|---|
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用 C-string 来构造 string 类对象 |
string(const char* s, size_t n) | 复制s所指字符序列中的前n个字符 |
string(const string& s) | 拷贝构造函数 |
string(size_t n, char c) | 生成n个c字符的 string 类对象 |
string(const string& str, size_t pos, size_t len = npos) | 复制str中从字符位置pos开始并跨越len个字符的部分 |
最后一个如果不给len,缺省值是npos,在定义中虽然npos = -1,但是npos是无符号数,所以是整数的最大值。
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s1;
string s2("hello world");
string s3("hello world", 5);
string s4(s2);//拷贝构造s2
string s5(5, 'c');//生成5个c字符的string类对象
string s6(s2, 6, 5);//从下标为6的位置开始向后复制5个字符
//在string类中流插入和流提取已经重载过了,可以直接使用
cout << s1 << endl; //空串
cout << s2 << endl; //hello world
cout << s3 << endl; //hello
cout << s4 << endl; //hello world
cout << s5 << endl; //ccccc
cout << s6 << endl; //world
return 0;
}
我们还可以这样:
string s = "hello world"
因为会发生隐式类型转换,单参数的构造函数支持隐式类型转换,const char *
转换成string
,编译器优化为直接构造,如果不想进行隐式类型转换我们可以在构造函数前加关键字explicit
。
string类对象容量操作
函数名称 | 功能说明 |
---|---|
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 检测字符串是否为空串,是返回true,否则返回false |
clear | 清空有效字符 |
reserve | 为字符串预留空间 |
resize | 将有效字符的个数改成n个,多出的空间用字符c填充 |
max_size | 获取string对象的最大长度 |
shrink_to_fit(C++11) | 缩容,将capacity缩小至和size一样大小 |
size/length函数
- 使用size函数或者length函数获取当前有效字符的个数
size_t size() const;
size_t length() const;
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
cout << s.size() << endl; // 11
cout << s.length() << endl; // 11
}
size函数和length函数作用相同,因为string类产生的比较早,后来为了和其他的数据结构保持一致,所以增加了size函数。
max_size函数
- 使用max_size函数获取string对象的最大长度
size_t max_size() const;
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
cout << s.max_size() << endl; // 2147483647
}
capacity函数
- 使用capacity函数获取当前对象所分配的存储空间的大小
size_t capacity() const;
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
cout << s.capacity() << endl; // 15
}
VS下面的容量空间是不包含\0
的。
扩展:string类的扩容规则:
我们通过下面的程序观察:
#include <string> #include <iostream> using namespace std; int main() { string s; size_t sz = s.capacity(); cout << "making s grow:" << sz << endl; for (int i = 0; i < 100; i++) { s.push_back('c'); if (sz != s.capacity()) { sz = s.capacity(); cout << "capacity changed :" << sz << "\n"; } } return 0; }
输出结果:
这个容量没有算上
\0
,从结果可以看出第一次扩容是2倍,往后都是1.5倍扩容。刚开始的时候没有动态开辟数组,而是存到了string类中的一个buff数组中,数组大小是16,包含
\0
,如果内容小于16字节就存在buff数组中,大于16就存到ptr指向的空间上去。所以实际上并不是第一次扩容是2倍,而是第一次开空间的时候直接开到32,再往后以后都是1.5倍扩容。string类对象的结构其实就像下面代码一样:
class string{ private: char* _ptr; char _buf[16]; size_t _size; size_t _capacity; }; cout << sizeof(s) << endl;//28,所以string类里面存的就是上面列出来的。
所以可以认为VS编译器下string是1.5倍的扩容。
Linux g++下是2倍扩容:
平台使用的STL的版本不一样,所以扩容规则也不同。
empty函数
- 使用empty函数判断字符串是否为空
bool empty() const;
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
cout << s.empty() << endl; // true
}
clear函数
- 使用clear函数清空有效字符,删除后对象变为空字符串
void clear();
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
cout << s.size() << endl; // 11
cout << s.capacity() << endl;//15
s.clear();
cout << s.size() << endl; // 0
cout << s.capacity() << endl; //15
}
resize函数
- 使用resize改变当前对象的有效字符的个数
void resize (size_t n);
void resize (size_t n, char c);
resize规则:扩容并且初始化
1.当n大于对象当前的size时,将size扩大到n,扩大的字符为c,若c未给出,则默认为’\0’。
2.当n小于对象当前的size时,将size缩小到n。
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
cout << s.size() << endl; // 11
cout << s.capacity() << endl; //15
cout << s << endl; //hello world
//n小于对象当前的size时,将size缩小到n
s.resize(5);
cout << s.size() << endl;//5
cout << s.capacity() << endl;//15
cout << s << endl; //hello
//n大于对象当前的size时,将size扩大到n,扩大的字符为c
s.resize(12, 'x');
cout << s.size() << endl;//12
cout << s.capacity() << endl;//15
cout << s << endl;//helloxxxxxxx
//当n大于对象当前的size时,将size扩大到n,扩大的字符为c,若c未给出,则默认为’\0’
s.resize(20);
cout << s.size() << endl;//20
cout << s.capacity() << endl;//31
cout << s << endl;//helloxxxxxxx
}
注意:若给出的n大于对象当前的capacity,则capacity也会根据规则进行扩大。
reserve函数
- 使用reserve改变当前对象的容量大小
void reserve (size_t n = 0);
reserve规则:
1、当n大于对象当前的capacity时,将capacity扩大到n或大于n。
2、当n小于对象当前的capacity时,capacity不变。
reserve函数对字符串的size没有影响,并且无法更改其内容。可以通过reserve函数提前开空间,减少扩容,提高效率。
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
cout << s.size() << endl; // 11
cout << s.capacity() << endl; //15
//n小于对象当前的capacity时,capacity不变
s.reserve(5);
cout << s.size() << endl;//11
cout << s.capacity() << endl;//15
//n大于对象当前的capacity时,将capacity扩大到n或者大于n
s.reserve(32);
cout << s.size() << endl;//11
cout << s.capacity() << endl;//47
}
shrink_to_fit函数
- shrink_to_fit是缩容,将capacity缩小至和size一样(C++11)
void shrink_to_fit();
注意:
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
- clear()只是将string中有效字符清空,不改变底层空间大小
- resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时用’\0’来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变容量大小,如果是将元素个数减少,容量大小不变
- reserve(size_t n = 0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的容量大小时,reserver不会改变容量大小。
string类对象的访问及遍历操作
函数名称 | 功能说明 |
---|---|
operator[pos] | 返回pos位置的字符,const string类对象调用 |
at(pos) | 返回pos位置的字符 |
begin+ end | begin获取第一个字符的迭代器 + end获取最后一个字符的下一个位置的迭代器 |
rbegin + rend | rbegin获取最后一个字符的迭代器 + rend获取第一个字符的前一个位置的迭代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
operator[]重载运算符
- operator[]
string类对[ ]运算符进行了重载,所以我们可以直接使用[ ]+下标访问对象中的元素。重载使用的引用返回,所以我们可以通过[ ]+下标修改对应位置的元素。
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
const string s1;
string s("hello world");
for (int i = 0; i < s.size(); i++)
{
//[]+下标访问字符串中的元素
cout << s[i];
}
cout << endl;
for (int i = 0; i < s.size(); i++)
{
//修改字符串中对应位置的元素
s[i] = 'x';
}
cout << s;
cout << endl;
return 0;
}
扩展:
成员函数后加const关键字后,我们称这个函数为常函数。
形式: void fun() const {};
特性:
构造函数和析构函数不可以是常函数;
可以使用数据成员,但是常函数内不可修改成员属性;
常函数的this指针类型是const 类名* this,正常的成员函数this指针类型是类名* this。
实例化对象前加const称该对象为常对象。形式:const 类名 对象名;常对象只能调用常函数。
at函数
- 使用at访问对象中的元素
at函数和[ ]+下标的作用相同,at函数使用的也是引用返回,所以我们也可以通过at函数修改对应位置的元素。
但是at和[]+下标的一个区别是:如果访问越界,[]+下标的方式会直接报错;而at是抛异常,异常可以被捕获。
char& at (size_t pos);
const char& at (size_t pos) const;
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
const string s1;
string s("hello world");
for (int i = 0; i < s.size(); i++)
{
//[]+下标访问字符串中的元素
cout << s.at(i);
}
cout << endl;
for (int i = 0; i < s.size(); i++)
{
//修改字符串中对应位置的元素
s.at(i) = 'x';
}
cout << s;//xxxxxxxxxxx
cout << endl;
return 0;
}
迭代器
- 使用迭代器访问对象中的元素
begin获取第一个字符的迭代器 + end获取最后一个字符下一个位置(即‘\0’)的迭代器:
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
rbegin获取最后一个字符的迭代器 + rend获取第一个字符的前一个位置的迭代器:
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
迭代器区间是左闭右开,无论是正向迭代器还是反向迭代器遍历的时候都是通过++操作。
使用正向迭代器begin和end访问修改元素:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
//使用正向迭代器访问元素
string::iterator it1 = s.begin();
while (it1 != s.end())
{
cout << *it1;
it1++;
}
cout << endl;
//使用正向迭代器访问元素并对其进行修改
string::iterator it2 = s.begin();
while (it2 != s.end())
{
*it2 += 1;
it2++;
}
cout << s;//ifmmp!xpsme
cout << endl;
return 0;
}
使用反向迭代器begin和end访问修改元素:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
//使用反向迭代器访问元素
string::reverse_iterator rit1 = s.rbegin();
while (rit1 != s.rend())
{
cout << *rit1;
rit1++;
}
//输出 dlrow olleh
cout << endl;
//使用反向迭代器访问元素并对其进行修改
string::reverse_iterator rit2 = s.rbegin();
while (rit2 != s.rend())
{
*rit2 += 1;
rit2++;
}
cout << s;//ifmmp!xpsme
cout << endl;
return 0;
}
当类对象为const对象的时候我们可以使用const迭代器:
const类型的迭代器不能修改类对象的内容,即不能通过*迭代器
方式修改字符串内容,但是可以修改迭代器本身(迭代器++
)。
const_iterator begin() const;
const_iterator end() const;
const_reverse_iterator rbegin() const;
const_reverse_iterator rend() const;
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
//使用const正向迭代器访问元素
const string s("hello world");
string::const_iterator it1 = s.begin();
while (it1 != s.end())
{
cout << *it1;
it1++;
}
//输出 hello world
cout << endl;
//使用const反向迭代器访问元素
string::const_reverse_iterator rit1 = s.rbegin();
while (rit1 != s.rend())
{
cout << *rit1;
rit1++;
}
//输出 dlrow olleh
cout << endl;
return 0;
}
范围for
- 使用范围for访问对象中的元素
若是需要通过范围for修改对象的元素,则用于接收元素的变量类型必须是引用类型,否则只是对象元素的拷贝,对变量的修改不会影响到对象的元素。
范围for的使用是建立在迭代器的基础上的,当迭代器发生错误范围for也将不能再使用。
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
for (auto e : s)
{
cout << e;
}
//输出 hello world
cout << endl;
//变量类型使用引用类型
for (auto& e : s)
{
e++;
cout << e;
}
//输出 ifmmp!xpsme
cout << endl;
return 0;
}
string类对象的修改操作
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
pop_back(C++11) | 尾删 |
append | 在字符串后追加一个字符串 |
operator+= | 在字符串后追加字符或者字符串 |
c_str | 返回C格式字符串 |
find + npos | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
find_first_of | 从字符串第一个位置开始向后查找与字符串中相同的字符 |
find_last_of | 从字符串最后一个位置开始向前查找与字符串中相同的字符 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
insert | 在字符串pos位置插入字符或字符串 |
swap | 交换两个字符串的内容 |
erase | 指定位置删除字符或字符串 |
replace | 从pos开始长度为len的内容替换成指定内容 |
copy | 将string的子字符串复制到字符数组中 |
push_back函数
- 使用push_back进行尾插
void push_back (char c);
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello");
cout << s << endl;//hello
s.push_back(' ');
s.push_back('w');
s.push_back('o');
s.push_back('r');
s.push_back('l');
s.push_back('d');
cout << s << endl;//hello world
return 0;
}
pop_back函数
- 尾删
void pop_back();
示例:
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string s("hello world!");
str.pop_back();
cout << s << endl;//输出hello world
return 0;
}
append函数
- 使用append进行追加
//在原字符串后面追加字符串str
string& append (const string& str);
//在原字符串后面追加C字符串s
string& append (const char* s);
//在原字符串后面追加n个字符s
string& append (size_t n, char c);
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello");
cout << s << endl;//hello
string s1("world");
在原字符串后面追加字符串s1
s.append(s1);
cout << s << endl;//helloworld
//在原字符串后面追加C字符串c
const char* c = "xxx";
s.append(c);
//s.append("xxx");
cout << s << endl;//helloworldxxx
//在原字符串后面追加n个字符s
s.append(2,'4');
cout << s << endl;//helloworldxxx44
return 0;
}
operator+=重载运算符
- 使用operator+=在字符串后追加字符或者字符串
//在原字符串后面追加字符串str
string& operator+= (const string& str);
//在原字符串后面追加C字符串s
string& operator+= (const char* s);
//在原字符串后面追加字符s
string& operator+= (char c);
示例:
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello");
cout << s << endl;//hello
string s1("world");
在原字符串后面追加字符串s1
s += s1;
cout << s << endl;//helloworld
//在原字符串后面追加C字符串c
const char* c = "xxx";
s +=c;
//s += "xxx";
cout << s << endl;//helloworldxxx
//在原字符串后面追加n个字符s
s += '4';
cout << s << endl;//helloworldxxx4
return 0;
}
c_str函数
- 返回C格式字符串
const char* c_str() const;
c_str()返回const char*
类型,返回的字符串会以空字符结尾。
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
//作为自定义类型,调用的是重载之后的流插入
cout << s << endl;//hello world
//c_str返回的是const char*类型,是指针,为内置类型
//内置类型调用的是库里面的流插入,char*类型按照C字符串去打印
cout << s.c_str() << endl;//hello world
}
两者的区别是什么呢?如果我们按照重载流插入去打印,是按照string类的size大小去打印的;但是当转换成C字符串之后使用库里的流插入时,遇见\0
就停止了。
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("hello world");
s += '\0';
s += '\0';
s += '\0';
s += '6';
s += '6';
s += '6';
cout << s << endl;//hello world666
cout << s.c_str() << endl;//hello world
}
data函数和c_str函数作用相同,用法也一致。
find函数
- 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置,如果找到了返回第一个字符匹配出现的位置,如果没有找到返回npos(因为字符串不会有npos那么长,所以不用担心比字符串npos大)
//从pos位置向后找与str匹配的第一个位置
size_t find (const string& str, size_t pos = 0) const;
//从pos位置向后找与s匹配的第一个位置
size_t find (const char* s, size_t pos = 0) const;
//从pos位置向后找与字符c匹配的第一个位置
size_t find (char c, size_t pos = 0) const;
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("http://www.cplusplus.com/reference/string/string/find/");
//find (const string& str, size_t pos = 0)正向搜索与string对象所匹配的第一个位置
string s2("www");
//从pos(pos缺省值为0)开始在s1中搜索与s2对象所匹配的第一个位置
cout << s1.find(s2) << endl; //7
// find (const char* s, size_t pos = 0)正向搜索与字符串s所匹配的第一个位置
const char* s = "cplusplus.com";
//从pos(pos缺省值为0)开始在s1中搜索与s对象所匹配的第一个位置;
cout << s1.find(s) << endl; //11
// find (char c, size_t pos = 0)正向搜索与字符char所匹配的第一个位置
cout << s1.find(':') << endl; //4
return 0;
}
rfind函数
- rfind函数和find函数作用类似,但是rfind是反向搜索第一个匹配项,如果找到了返回第一个字符匹配出现的位置(从首字符开始向后所在位置),如果没有找到返回npos(因为字符串不会有npos那么长,所以不用担心比字符串npos大)
//从npos位置开始向前搜索字符串str,返回第一次出现的位置
size_t rfind (const string& str, size_t pos = npos) const;
//从npos位置开始向前搜索字符串s,返回第一次出现的位置
size_t rfind (const char* s, size_t pos = npos) const;
//从npos位置开始向前搜索字符c,返回第一次出现的位置
size_t rfind (char c, size_t pos = npos) const;
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("http://www.cplusplus.com/reference/string/string/find/");
//find (const string& str, size_t pos = 0)正向搜索与string对象所匹配的第一个位置
string s2("www");
//从pos(pos缺省值为0)开始在s1中搜索与s2对象所匹配的第一个位置
cout << s1.rfind(s2) << endl; //7
// find (const char* s, size_t pos = 0)正向搜索与字符串s所匹配的第一个位置
const char* s = "string";
//从pos(pos缺省值为0)开始在s1中搜索与s对象所匹配的第一个位置;
cout << s1.rfind(s) << endl; //42
// find (char c, size_t pos = 0)正向搜索与字符char所匹配的第一个位置
cout << s1.rfind(':') << endl; //4
return 0;
}
find_first_of函数
- 从字符串首个位置开始向后查找与字符串中相同的字符,只要有相同字符就返回,和子串区分开;查找成功返回匹配位置,不同返回npos
//从字符串首个位置开始向后查找与字符串str中相同的字符
size_type find_first_of (const basic_string& str, size_type pos = 0) const;
//从字符串首个位置开始向后查找与字符串s中相同的字符
size_type find_first_of (const charT* s, size_type pos = 0) const;
//从字符串pos位置开始向后查找与字符串s中相同的字符
size_type find_first_of (const charT* s, size_type pos, size_type n) const;
//从字符串首个位置开始向后查找与字符c相同的坐标并返回
size_type find_first_of (charT c, size_type pos = 0) const;
示例1:
//从字符串首个位置开始向后查找与字符串str中相同的字符
size_type find_first_of (const basic_string& str, size_type pos = 0) const;
代码:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello world");
string s2("hl");
//在s1字符串中查找与s2字符串中匹配的字符
//成功返回下标位置,失败返回npos
size_t pos1 = s1.find_first_of(s2);
while (pos1 != string::npos)
{
//将pos1位置处对应的字符替换成*
s1[pos1] = '*';
//从pos+1位置开始向后查找
pos1 = s1.find_first_of(s2, pos1 + 1);
}
cout << s1 << endl; //*e**o wor*d
}
示例2:
//从字符串首个位置开始向后查找与字符串s中相同的字符
size_type find_first_of (const charT* s, size_type pos = 0) const;
//从字符串首个位置开始向后查找与字符c相同的坐标并返回
size_type find_first_of (charT c, size_type pos = 0) const;
代码:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello world");
const char* s = "hl";
size_t pos1 = s1.find_first_of(s);
while (pos1 != string::npos)
{
//将pos1位置处对应的字符替换成*
s1[pos1] = '*';
//从pos+1位置开始向后查找
pos1 = s1.find_first_of(s, pos1 + 1);
}
cout << s1 << endl; //*e**o wor*d
//查找与字符e相同的字符并返回下标
size_t pos2 = s1.find_first_of('e');
cout << pos2 << endl;//1
}
find_last_of函数
从字符串最后一个位置开始向前查找与字符串中相同的字符
//从字符串最后位置开始向前查找与字符串str中相同的字符
size_type find_last_of (const basic_string& str, size_type pos = npos) const;
//从字符串最后一个位置开始向前查找与字符串s中相同的字符
size_type find_last_of (const charT* s, size_type pos = npos) const;
//从字符串pos位置开始向前查找与字符串s中相同的字符
size_type find_last_of (const charT* s, size_type pos, size_type n) const;
//从字符串最后一个位置开始向前查找与字符c相同的坐标并返回
size_type find_last_of (charT c, size_type pos = npos) const;
示例1:
//从字符串最后位置开始向前查找与字符串str中相同的字符
size_type find_last_of (const basic_string& str, size_type pos = npos) const;
代码:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello world");
string s2("hl");
//在s1字符串中查找与s2字符串中匹配的字符
//成功返回下标位置,失败返回npos
size_t pos1 = s1.find_last_of(s2);
while (pos1 != string::npos)
{
//将pos1位置处对应的字符替换成*
s1[pos1] = '*';
//从pos+1位置开始向前查找
pos1 = s1.find_last_of(s2, pos1 + 1);
}
cout << s1 << endl; //*e**o wor*d
}
示例2:
//从字符串pos位置开始向前查找与字符串s中相同的字符
size_type find_last_of (const charT* s, size_type pos, size_type n) const;
//从字符串最后一个位置开始向前查找与字符c相同的坐标并返回
size_type find_last_of (charT c, size_type pos = npos) const;
代码:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello world");
const char* s = "hl";
size_t pos1 = s1.find_last_of(s);
while (pos1 != string::npos)
{
//将pos1位置处对应的字符替换成*
s1[pos1] = '*';
//从pos+1位置开始向后查找
pos1 = s1.find_last_of(s, pos1 + 1);
}
cout << s1 << endl; //*e**o wor*d
//查找与字符e相同的字符并返回下标
size_t pos2 = s1.find_last_of('e');
cout << pos2 << endl;//1
}
substr函数
- 获取字符串中位置pos开始的长度为len的子串,返回值类型为string
string substr (size_t pos = 0, size_t len = npos) const;
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello world");
string s2 = s1.substr(6, 5);
cout << s2 << endl;//world
}
insert函数
- 在字符串pos位置插入字符或字符串
//在pos位置插入字符串str
string& insert (size_t pos, const string& str);
//在pos位置插入字符串s
string& insert (size_t pos, const char* s);
//参数是迭代器,在迭代器指定的位置插入字符c
iterator insert (iterator p, char c);
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello");
string s2("every");
//插入字符串s2
s1.insert(5,s2);
cout << s1 << endl;//helloevery
//find和insert配合,插入字符
s1.insert(s1.find('y') + 1, "body");
cout << s1 << endl;//helloeverybody
//使用迭代器在字符串的后面插入字符
s1.insert(s1.end(), '!');
cout << s1 << endl;//helloeverybody!
}
swap函数
- 使用string类中提供的swap函数完成两个string字符串的交换
底层是指针,改变的是指针的指向和size的值。
void swap (string& str);
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello");
string s2("every");
cout << s1 << endl;//hello
cout << s2 << endl;//every
s1.swap(s2);
cout << s1 << endl;//every
cout << s2 << endl;//hello
}
string类中提供了swap函数,算法库(头文件为algorithm)中也提供了一个swap函数:
void swap (string& x, string& y);
示例:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string s1("hello");
string s2("every");
cout << s1 << endl;//hello
cout << s2 << endl;//every
swap(s1,s2);//使用算法库中的swap函数
cout << s1 << endl;//every
cout << s2 << endl;//hello
}
两者的区别是string类中提供的只能针对string类型,算法库中的swap函数无论什么类型都可以使用。
对于string对象来说string提供的swap函数更高效,直接改string指针的指向就可以了;库里面提供的函数交换时会产生一个临时对象,拷过去再赋值。
erase函数
- 指定位置删除字符或字符串
//从位置pos开始删除len长度
string& erase (size_t pos = 0, size_t len = npos);
//删除迭代器指向位置的字符
iterator erase (iterator p);
//删除迭代器first开始到last-1之间的字符,迭代器是左闭右开,不包括last位置的字符
iterator erase (iterator first, iterator last);
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello everybody");
//删除从0开始的6个字符
s1.erase(0, 6);
cout << s1 << endl;//everybody
//删除迭代器指向位置的字符e
s1.erase(s1.begin());
cout << s1 << endl;//verybody
s1.erase(s1.begin(),s1.end());
cout << s1 << endl;//空字符串
}
replace函数
- 从pos位置开始长度为len的内容替换成指定内容
//将从pos位置开始的长度为len的内容替换为字符串s
string& replace (size_t pos, size_t len, const char* s);
//将从pos位置开始的长度为len的内容替换为n个字符c
string& replace (size_t pos, size_t len, size_t n, char c);
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello everybody");
s1.replace(6, 9, "world");
cout << s1 << endl;//hello world
s1.replace(0, 5, 4, 'I');//IIII world
cout << s1 << endl;
}
copy函数
- 将string的子字符串复制到字符数组中,返回拷贝字符长度
size_t copy (char* s, size_t len, size_t pos = 0) const;
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello everybody");
char s[20];
//将s1中从位置0开始长度为5的字符内容拷贝到字符数组s中
size_t length = s1.copy(s, 5, 0);
//copy函数仅仅只是拷贝
//并不会在字符数组结尾处加\0,需要自己手动加
s[length] = '\0';
cout << s << endl;
}
string类的非成员函数
函数名称 | 功能说明 |
---|---|
operator+ | 在字符串后添加指定字符或者字符串 |
operator>> | 输入运算符重载 |
operator<< | 输出运算符重载 |
getline | 获取一行字符串 |
relational operators | 大小比较 |
operator+
- 在字符串后添加指定字符或者字符串
尽量少用,因为传值返回,导致深拷贝效率低
//两个参数是两个字符串类对象
string operator+ (const string& lhs, const string& rhs);
//两个参数一个是字符串类对象,一个是c字符串,交换位置也可以使用
string operator+ (const string& lhs, const char* rhs);
string operator+ (const char* lhs, const string& rhs);
//两个参数一个是字符串类对象,一个是c字符,交换位置也可以使用
string operator+ (const string& lhs, char rhs);
string operator+ (char lhs, const string& rhs);
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello");
string s2("world");
const char* s3 = "everybody";
//两个参数是string类型
s1 = s1 + s2;
cout << s1 << endl;//helloworld
//参数一个是C字符串一个是string类型,和操作数位置无关
s1 = s3 + s1;
//s1 = s1 + s3;
cout << s1 << endl;//everybodyhelloworld
//参数一个是C字符一个是string类型,和操作数位置无关
s1 = s1 + 'x';
//s1 = 'x' + s1;
cout << s1 << endl;//everybodyhelloworldx
}
operator>>和operator<<
istream& operator>> (istream& is, string& str);//重载流提取
ostream& operator<< (ostream& os, const string& str);//重载流插入
示例:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello");
string s2;
//支持连续输入和输出,输入会覆盖之前的内容
cin >> s1 >> s2;//输入hello world
cout << s1 << " " << s2;//输出hello world
}
getline函数
- 使用cin>>输入的时候遇到空格便会停止读取,所以当我们需要输入带有空格的字符串时可以使用getline函数,getline函数只有在遇到
\n
的时候才会停止读取。
//当读取到换行符\n时停止读取
istream& getline (istream& is, string& str);
//可以自己设置分隔符delim
istream& getline (istream& is, string& str, char delim);
示例:
使用>>:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1;
//使用>>
cin >> s1;//输入hello world
cout << s1;//输出hello
}
使用getline函数:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1;
//使用getline函数,当遇到换行时停止读取
getline(cin, s1);//输入hello world
cout << s1 << endl;//输出hello world
//设置分隔符为!,当输入!或者换行时停止读取
getline(cin, s1, '!');//输入hello world! nihao
cout << s1 << endl;//输出hello world
}
输入的时候cin>>通过换行或者空格来区分多个值;当输入有空格的字符串时,最好使用getline,getline遇到换行结束。
关系运算符
因为string类中重载了大量的关系运算符relational operators,所以我们平时不用string中的compare函数比较。文章来源:https://www.toymoban.com/news/detail-507167.html
//==重载运算符
bool operator== (const string& lhs, const string& rhs);
bool operator== (const char* lhs, const string& rhs);
bool operator== (const string& lhs, const char* rhs);
//!=重载运算符
bool operator!= (const string& lhs, const string& rhs);
bool operator!= (const char* lhs, const string& rhs);
bool operator!= (const string& lhs, const char* rhs);
// < 重载运算符
bool operator< (const string& lhs, const string& rhs);
bool operator< (const char* lhs, const string& rhs);
bool operator< (const string& lhs, const char* rhs);
//<=重载运算符
bool operator<= (const string& lhs, const string& rhs);
bool operator<= (const char* lhs, const string& rhs);
bool operator<= (const string& lhs, const char* rhs);
// > 重载运算符
bool operator> (const string& lhs, const string& rhs);
bool operator> (const char* lhs, const string& rhs);
bool operator> (const string& lhs, const char* rhs);
//>=重载运算符
bool operator>= (const string& lhs, const string& rhs);
bool operator>= (const char* lhs, const string& rhs);
bool operator>= (const string& lhs, const char* rhs);
示例:文章来源地址https://www.toymoban.com/news/detail-507167.html
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello world");
string s2("hello");
char s3[] = "str";
const char* s4 = "why";
//上面四个字符串只要保证运算符中有一个string类即都可使用重载运算符
cout << (s3 == s2) << endl;//0
cout << (s1 != s2) << endl;//1
cout << (s1 > s2) << endl;//1
cout << (s1 >= s2) << endl;//1
//……此处省略不一一列举
}
到了这里,关于【C++】STL——string类详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!