【C++】STL——string类详解

这篇具有很好参考价值的文章主要介绍了【C++】STL——string类详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。
🚁 个人主页:不 良
🔥 系列专栏:🛸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提供六大组件,它们之间可以相互组合使用。

【C++】STL——string类详解

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

【C++】STL——string类详解

【C++】STL——string类详解

string管理的是char(1个字节)的数组;wstring管理的是wchar_t(2个字节)的数组;u16string管理的是char16_t(2个字节)的数组;u32string管理的是char32_t(4个字节)的数组。为什么会有这样的差异?因为现实世界中有管理不同字符数组的需求的。

文档中的一些主要模块如下图,我们平时学习STL时可以使用这个网站:Cplusplus

【C++】STL——string类详解

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

输出结果:

【C++】STL——string类详解

这个容量没有算上\0,从结果可以看出第一次扩容是2倍,往后都是1.5倍扩容。

刚开始的时候没有动态开辟数组,而是存到了string类中的一个buff数组中,数组大小是16,包含\0,如果内容小于16字节就存在buff数组中,大于16就存到ptr指向的空间上去。所以实际上并不是第一次扩容是2倍,而是第一次开空间的时候直接开到32,再往后以后都是1.5倍扩容。

【C++】STL——string类详解

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倍扩容:

【C++】STL——string类详解

平台使用的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;

【C++】STL——string类详解

迭代器区间是左闭右开,无论是正向迭代器还是反向迭代器遍历的时候都是通过++操作

使用正向迭代器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函数比较。

//==重载运算符
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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++深入浅出】STL之string用法详解

    目录 一. 前言 二. STL概要 2.1 什么是STL 2.2 STL的六大组件 2.3 STL的缺陷 三. string类概述 3.1 什么是string类 3.2 为什么要使用string类 四. string类的使用 4.1 包含头文件 4.2 构造函数 4.3 赋值运算符重载 4.4 容量操作 4.5 访问/遍历操作 4.6 查找修改操作 4.7 子串操作 ​4.8 非成员函数  

    2024年02月05日
    浏览(36)
  • 【C++初阶】STL详解(二)string类的模拟实现

    本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C++ 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 注:为了防止与标准库当中的string类产生命名冲

    2024年02月05日
    浏览(39)
  • 【c++】STL--string

            最开始我们学习c语言的时候,我们发现刷题或者写代码都是比较麻烦的,如果说用c语言造一辆车,那么我需要用c语言先把轮子造好--各个零件,当我们造好之后再组装。那么c++则是造好了轮子,只需要我们组装就好了。这里的的STL里有各个组件,我只要熟悉的掌握

    2024年02月03日
    浏览(38)
  • C++ STL string类

    目录 一.为什么学习string类 (1) C语言中的字符串 (2)标准库里面的string类 二. string类的常用接口说明 (1)string类对象的常见构造 (2)string类对象的容量操作 1.size(),length(). 2. capacity() 3.empty()  4.clear()  5.reserve()  6.resize() (3)string类对象的访问及遍历操作  1.operator[ pos

    2024年02月13日
    浏览(26)
  • 【C++】STL之string类(1)

    个人主页:平行线也会相交💪 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【C++之路】💌 本专栏旨在记录C++的学习路线,望对大家有所帮助🙇‍ 希望我们一起努力、成长,共同进步。🍓 接下来就开始STL部分的学习了,本文先来学习一下

    2024年02月11日
    浏览(33)
  • 【C++ • STL】探究string的源码

    ヾ(๑╹◡╹)ノ\\\" 人总要为过去的懒惰而付出代价 ヾ(๑╹◡╹)ノ\\\" 浅拷贝 :也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,

    2024年02月08日
    浏览(27)
  • C++ [STL之string模拟实现]

    本文已收录至《C++语言》专栏! 作者:ARMCSKGT 前面我们介绍了STL容器string的部分接口使用,有了string使我们对字符串的操作如鱼得水,其实string不止于使用方便,其实现也有许多我们值得学习的地方,本节将为您介绍string常用接口的代码实现! 本文接口的实现借助于C++官方

    2024年02月05日
    浏览(31)
  • 【C++精华铺】9.STL string

    目录 1. string类的优势 2. string类的常用接口 2.1 常用构造 1. 空串构造:string(); 2. C串构造:string(const char* s); 3. 拷贝构造:string(const string str); 4. 字符填充构造:string(size_t n, char c); 5. 迭代器构造:string(InputIterator first, InputIterator last); 2.2  string容量操作 1. size_t size(),size_t length

    2024年02月11日
    浏览(25)
  • C++ [STL之string的使用]

    本文已收录至《C++语言》专栏! 作者:ARMCSKGT 字符串在程序中经常出现,C语言为此提供了很多字符串操作函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问,于是STL单独为字符串实现了一个容器,

    2024年02月01日
    浏览(24)
  • C++ STL string类模拟实现

    目录 string类成员变量 一.构造函数 二.析构函数 三.拷贝构造 四.size(),capacity() 五.operator [ ] 六. operator =  七.字符串比较  八.reserve() 九.push_back(),append() 十.operator+=  十一.insert()  十二.迭代器  十二.erase() 十三.swap()  十四.find() 十五.流提取,流输出 十六

    2024年02月14日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包