目录
- 1.为什么学习string类
- 1.1C语言中的字符串
- 1.2OJ题中的优势
- 2.标准库中的string类
- 3.string类的常用接口函数
- 3.1string类对象的常见构造
- 3.2string类对象的容量操作
- 3.3string类对象的访问及遍历操作
- 3.4string类的修改操作
- 3.5string类的非成员函数
- 总结
1.为什么学习string类
1.1C语言中的字符串
C语言中,字符串是以
'\0'
结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想
,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
1.2OJ题中的优势
在
OJ
中,有关字符串的题目基本以string类
的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类
,很少有人去使用C库中的字符串操作函数。
2.标准库中的string类
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
string类
是使用char
作为它的字符类型,即使用它的默认char_traits
和分配器类型(关于模板的更多信息,请参阅basic_string
)。string类
是basic_string
模板类的一个实例,它使用char
来实例化basic_string
模板类,并用char_traits
和allocator
作为basic_string
的默认参数(根于更多的模板信息请参考basic_string
)。- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如
UTF-8
)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码字符)来操作。
总结:
①
string
是表示字符串的字符串类 ② 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string
的常规操作。
③string
在底层实际是:basic_string
模板类别名,typedefbasic_string<char
,char_traits
,
allocator>string
; ④不能操作多字节或者变长字符的序列。
⑤在使用string类
时,必须包含#include
头文件以及using namespace std
3.string类的常用接口函数
3.1string类对象的常见构造
string类的构造函数如下:
介绍经常使用的构造函数:
string();//用空字符串构造一个空string类对象,长度为0
string (const char* s);//用C语言字符串构造string类对象
string (size_t n, char c);//用n个字符c构造string类对象
string (const string& str);//用一个string类str拷贝构造string类对象
string (const string& str, size_t pos, size_t len = npos);
//从str对象pos位置拷贝len字节内容构造string类对象,如果str对象很短或len=npos,则拷贝str对象末尾
static const size_t npos = -1;
//npos为静态成员变量,-1存储在无符号整型中会发生整型提升,为整型的最大值
栗子:
void Test1()
{
string str1;
cout << str1 << endl;
string str2("hello world");
cout << str2 << endl;
string str3(10, 'x');
cout << str3 << endl;
string str4(str2);
cout << str4 << endl;
string str5(str2, 6);
cout << str5 << endl;
}
代码编译运行的结果为:
string类赋值运算符重载
和内置类型赋值运算符一样,string类赋值运算符重载,会将赋值对象原有的数据进行覆盖赋值!
string (1)
string& operator= (const string& str);
//用string类str对象进行赋值
c-string (2)
string& operator= (const char* s);
//用C字符串进行赋值
character (3)
string& operator= (char c);
//用C字符进行赋值
eg:
void Test2()
{
string str1("hello world");
cout << str1 << endl;
string str2;
str2 = str1;
cout << str2 << endl;
str2 = "xxxxx";
cout << str2 << endl;
str2 = 'y';
cout << str2 << endl;
}
代码编译运行的结果为:
3.2string类对象的容量操作
size_t size() const;//以字节为单位返回字符串的有效长度
size_t length() const;//以字节为单位返回字符串的有效长度
size_t capacity() const;//以字节形式返回string类存储的容量大小
bool empty() const;//检测字符串是否为空串,是返回true,否则返回false
eg1:
void Test3()
{
string str1("hello world");
printf("string类的大小为:");
cout << str1.size() << endl;
printf("string类的大小为:");
cout << str1.length() << endl;
printf("string类的存储容量为:");
cout << str1.capacity() << endl;
printf("string类的判空值为:");
cout << str1.empty() << endl;
}
代码编译运行的结果为:
size()与length()
方法底层实现原理完全相同,引入size()
的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
。
void clear();//清空有效字符串的内容,使string类对象变为空字符串
void resize (size_t n);
void resize (size_t n, char c);
//将string类有效字符数调整为n,多出的空间为用指定的字符c进行初始化,如果不指定则用'\0'进行初始化
void reserve (size_t n = 0);//为string类预留n字节存储空间大小
eg2:
void Test4()
{
string str1("hello world");
printf("string对象的内容为:");
cout << str1 << endl;
printf("string类的字符串大小为:");
cout << str1.size() << endl;
printf("string类当前的存储容量为:");
cout << str1.capacity() << endl;
cout << endl;
str1.resize(20, 'x');//调整有效字符的个数--调大
printf("string对象的内容为:");
cout << str1 << endl;
printf("string类的字符串大小为:");
cout << str1.size() << endl;
printf("string类当前的存储容量为:");
cout << str1.capacity() << endl;
cout << endl;
str1.reserve(50);//调整存储空间的大小--调大
printf("string对象的内容为:");
cout << str1 << endl;
printf("string类的字符串大小为:");
cout << str1.size() << endl;
printf("string类当前的存储容量为:");
cout << str1.capacity() << endl;
cout << endl;
str1.clear();//清除有效字符大小
printf("string对象的内容为:");
cout << str1 << endl;
printf("string类的字符串大小为:");
cout << str1.size() << endl;
printf("string类当前的存储容量为:");
cout << str1.capacity() << endl;
}
代码编译运行的结果为:
clear()
只是将string
中有效字符清空,不改变底层空间大小。resize(size_t n)
与resize(size_t n, char c)
都是将字符串中有效字符个数改变到n
个,不同的是当字符个数增多时:resize(n)
用'\0'
来填充多出的元素空间,resize(size_t n, char c)
用字符c来填充多出的元素空间。
eg3:
void Test5()
{
string str1("hello world");
printf("string对象的内容为:");
cout << str1 << endl;
printf("string类的字符串大小为:");
cout << str1.size() << endl;
printf("string类当前的存储容量为:");
cout << str1.capacity() << endl;
cout << endl;
str1.resize(6);//调整有效字符的个数--调小
printf("string对象的内容为:");
cout << str1 << endl;
printf("string类的字符串大小为:");
cout << str1.size() << endl;
printf("string类当前的存储容量为:");
cout << str1.capacity() << endl;
cout << endl;
str1.reserve(6);//调整存储空间的大小--调小
printf("string对象的内容为:");
cout << str1 << endl;
printf("string类的字符串大小为:");
cout << str1.size() << endl;
printf("string类当前的存储容量为:");
cout << str1.capacity() << endl;
cout << endl;
}
代码编译运行的结果为:
注意:
4.
resize
在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
5. 对string
操作时,如果能够大概预估到放多少字符,可以先通过reserve
把空间预留好
6.reserve(size_t res_arg=0)
:为string预留空间,不改变有效元素个数,当reserve
的参数小于string
的底层空间总大小时,reserve
不会改变容量大小
eg4:
void Test6()
{
string str1("hello world");
size_t old = str1.capacity();
cout << "初始容量为:" << str1.capacity() << endl;
for (int i = 0; i <= 100; i++)
{
str1 += 'x';
if (old != str1.capacity())
{
cout << "扩容:" << str1.capacity() << endl;
old = str1.capacity();
}
}
}
代码编译运行的结果为:
6.在VS2019编译器下,string类以原有容量的1.5倍进行扩容。
3.3string类对象的访问及遍历操作
函数名称:
char& operator[] (size_t pos);
//可以对返回string类对象pos位置的字符进行读写
const char& operator[] (size_t pos) const;
//只能对const string类对象pos位置的字符读取操作,不饿进行修改
eg1:
void Test8()
{
string str1("hello world");
//对修改前的str1对象进行读取操作
for (int i = 0; i < str1.size(); i++)
{
cout << str1[i];
}
cout << endl;
//对str1对象进行修改操作
for (int i = 0; i < str1.size(); i++)
{
str1[i]++;//等价于str.operator[i]++
cout << str1[i];
}
cout << endl;
}
代码编译运行的结果为:
函数名称:
char& at (size_t pos);
//可以对返回string类对象pos位置的字符进行读写
const char& at (size_t pos) const;
//只能对const string类对象pos位置的字符读取操作,不饿进行修改
eg2:
void Test9()
{
string str1("hello world");
//对修改前的str1对象进行读取操作
for (int i = 0; i < str1.size(); i++)
{
cout << str1.at(i);
}
cout << endl;
//对str1对象进行修改操作
for (int i = 0; i < str1.size(); i++)
{
str1.at(i)++;
cout << str1.at(i);
}
cout << endl;
}
代码编译运行的结果为:
函数operator[]与函数at的区别:
eg3:
void Test10()
{
//operator[]越界访问
string str1("hello world");
str1[12]++;
}
代码编译运行的结果为:
eg4:
void Test11()
{
//函数at越界访问
string str1("hello world");
try//捕获异常
{
str1.at(12)++;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
}
代码编译运行的结果为:
在
string
类对象越界访问的时候,函数operator[]
通过断言assert
报错,函数at
通过抛异常报错误。
函数名称:
iterator begin();
const_iterator begin() const;
//begin获取string类第一个字符的迭代器(从左到右)
iterator end();
const_iterator end() const;
//end为获取最后一个字符下一个位置的迭代器(从左到右)
图形理解:
eg5:
void Test12()
{
string str1("hello world");
string::iterator it = str1.begin();
while (it != str1.end())
{
cout << (*it);
it++;
}
cout << endl;
}
代码编译运行的结果为:
汇编代码:
eg6:
void Test13()
{
string str1("hello world");
for (auto& ch:str1)
{
cout << ch;
}
cout << endl;
}
代码编译运行的结果为:
汇编代码:
①迭代器是像指针一样的类型,可能是指针;②
iterator
(迭代器)提供一种统一的方式访问和修改容器的数据,使算法可以通过迭代器去处理容器中的数据;③范围for
的底层是通过迭代器实现的。
eg7:
void Test14()
{
//string类使用迭代器
string str1("hello world");
cout << "string类使用迭代器遍历的结果:" << endl;
string::iterator it = str1.begin();
while (it != str1.end())
{
cout << *it;
it++;
}
cout << endl;
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
cout << "vector使用迭代器遍历的结果:" << endl;
vector<int>::iterator vit = v.begin();
while (vit != v.end())
{
cout << *vit << " ";
++vit;
}
cout << endl;
list<int> lt;
lt.push_back(10);
lt.push_back(20);
lt.push_back(30);
lt.push_back(40);
lt.push_back(50);
cout << "list使用迭代器遍历的结果:" << endl;
list<int>::iterator lit = lt.begin();
while (lit != lt.end())
{
cout << *lit << " ";
++lit;
}
cout << endl;
}
代码编译运行的结果为:
①任何容器都支持迭代器,并且用法类似;②迭代器跟算法进行配合。
函数名称:
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
//string类逆置字符串的第一个字符的迭代器(即指向string类从右向左的第一个字符)
reverse_iterator rend();
const_reverse_iterator rend() const;
//string类逆置字符串的最后一个字符下一个位置的迭代器(即指向string类从右向左的最后一个字符的下一个位置)
图形理解
eg8:
void Test15()
{
string str1("hello world");
string::reverse_iterator it = str1.rbegin();
while (it != str1.rend())
{
cout << *it;
it++;
}
cout << endl;
}
代码编译运行的结果为:
①使用反向迭代器,从
string
类后面向前遍历;②范围for
不支持反向迭代器。
3.4string类的修改操作
函数名称:
void push_back (char c);//在字符串后尾插字符c
string (1)
string& operator+= (const string& str);
//在字符串后追加类对象str
c-string (2)
string& operator+= (const char* s);
//在字符串后追加一个字符串s
character (3)
string& operator+= (char c);
//在字符串后追加一个字符c
eg1:
void Test16()
{
string str1("hello world");
str1.push_back('x');
printf("使用push_back追加一个字符后:\n");
cout << str1 << endl;
string str2("yyyyy");
str1 += str2;
printf("使用operator+=追加str类对象:\n");
cout << str1 << endl;
str1 += "abcdef";
printf("使用operator+=追加字符串:\n");
cout << str1 << endl;
str1 += 'u';
printf("使用operator+=追加一个字符:\n");
cout << str1 << endl;
}
代码编译运行的结果为:
函数名称:
string (1)
string& append (const string& str);
//string类对象追加string类对象的字符串
substring (2)
string& append (const string& str, size_t subpos, size_t sublen);
//string类对象追加string类subpos位置sublen长度的字符串
c-string (3)
string& append (const char* s);
//string类对象追加一个常量字符串
buffer (4)
string& append (const char* s, size_t n);
//string类对象追加常量字符串的前n个字符
fill (5)
string& append (size_t n, char c);
//string类对象追加n个字符
range (6)
template <class InputIterator>
string& append (InputIterator first, InputIterator last);
//string类对象追加一个string类[first,last)区间的字符串
eg2:
void Test17()
{
string str1("hello world");
string str2(" fighting");
str1.append(str2);
cout << str1 << endl;
}
代码编译运行的结果为:
注意:
- 在
string
尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'
三种的实现方式差不多,一般情况下string
类的+=操作
用的比较多,+=操作
不仅可以连接单个字符,还可以连接字符串。
函数名称:
size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
//从stringpos位置往后查找str对象(字符串),并返回第一次出现的位置
eg3:
void Test18()
{
string str1("hello world");
size_t ret=str1.find("world");
cout << ret << endl;
}
代码编译运行的结果为:
函数名称:
size_t rfind (const string& str, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos = npos) const;
//从stringpos位置往前查找str对象(字符串),并返回第一次出现的位置
//返回的下标是string类的正向下标(从前往后数),而不是逆向的下标(从后往前数)
eg4:
void Test19()
{
string str1("hello world");
size_t ret = str1.rfind("world");
cout << ret << endl;
}
代码编译运行的结果为:
函数名称:
string substr (size_t pos = 0, size_t len = npos) const;
//返回string类从pos位置len长度的字符串构造的对象
eg5:
void Test20()
{
string str1("hello world");
string str2 = str1.substr(6);
cout << str2 << endl;
}
代码编译运行的结果为:
函数名称:
const char* c_str() const;
//返回指向string类C语言字符串
eg6:
void Test21()
{
string str1("hello world");
cout << str1.c_str() << endl;
}
代码编译运行的结果为:
3.5string类的非成员函数
函数名称:
string operator+ (const string& lhs, const string& rhs);
string operator+ (const string& lhs, const char* rhs);
//string类+运算符重载,使用传值返回,深拷贝效率低
eg1:
void Test22()
{
string str1("hello world");
string str2(" fighting");
string str3 = str1 + str2;
cout << str3 << endl;
str3 = str1 + " abc";
cout << str3 << endl;
}
代码编译运行的结果为:
函数名称:
istream& operator>> (istream& is, string& str);//string类输入运算符重载,遇到空格、'\n',停止读取
ostream& operator<< (ostream& os, const string& str);//string类输出运算符重载
eg2:
void Test23()
{
string str1;
cin >> str1;//使用operator>>运算符重载读取(遇到空格、'\n'停止读取)
cout << str1 << endl;//使用operator<<运算符重载写入
}
代码编译运行的结果为:
函数名称:
istream& getline (istream& is, string& str, char delim);
istream& getline (istream& is, string& str);
//获取一行字符串给string类对象,可以指定delim终止符,不指定则默认以'\n'为终止符
eg3:
void Test24()
{
string str1;
getline(cin, str1);
cout << str1 << endl;
}
代码编译运行的结果为:
总结
本章节我们一起学习了string类常用接口函数,希望对大家了解string类有些许帮助,感谢大家阅读,若有不对欢迎纠正!🎠🎠🎠