【C++之STL】一文学会使用 string
文章目录
- 1. STL导读
- 1. 1 什么是STL
- 1. 2 STL的版本
- 1. 3 STL六大组件
- 1. 4 STL的重要性
- 1. 5 STL的学习
- 1. 6 STL系列博客的规划
- 2. string
- 2. 1 为什么学习string类?
- 2. 2 标准库中的string
- 2. 3 基本构造
- 2. 4 尾插与输出运算符重载
- 2. 5 构造函数
- 2. 6 赋值运算符重载
- 2. 7 容量操作
- 2. 8 元素访问
- 2. 9 迭代器
- 2. 10 修改
- 2. 11 字符串操作
- 2. 12 不在类中的函数重载
- 3. 尾声
1. STL导读
1. 1 什么是STL
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
1. 2 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,devc++等)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程风格上看,阅读性非常高。学习STL时需要阅读的源代码,主要参考的就是这个版本。
1. 3 STL六大组件
顺带一提:string并不是STL标准所规定的容器,实际上string的出现比STL早,可以说是STL容器标准制作的探路石,因此相对其它容器,string的接口显得较为臃肿,命名风格较为奇特,但这并不妨碍它的强大。
1. 4 STL的重要性
在C++入门基础介绍(上)这篇博客中我提到过,C++的第一个正式版本的颁布曾因为STL的出现而延误:
C++的标准化工作于1989年开始,并成立了一个ANSI和IS0(International
StandardsOrganization)国际标准化组织的联合标准化委员会。1994年标准化委员会提出了第一个标准化草案。在该草案中,委员会在保持斯特劳斯特卢普最初定义的所有特征的时,还增加了部分新特征。
在完成C++标准化的第一个草案后不久,STL(Standard Template Library)诞生了,STL是惠普实验室开发的一系软件的统称。它是由Alexander Stepanov、MengLee和David RMusser在惠普实验室工作时所开发出来的。在通过了标准化第一个草案之后,联合标准化委员会又投票并通过了将STL包含到C++标准中的提议。STL对C++的扩展超出C++的最初定义范围。虽然在标准中增加STL是个很重要的决定,但也因此延缓了C++标准化的进程。
1997年11月14日,联合标准化委员会通过了该标准的最终草案。1998年,C++的ANSI/IS0标准被投入使用。
接近三年的等待,足以让任何人认识到它的重要性。
事实上也确实如此,且功利地说一说,在任何招聘C++开发岗位的笔试与面试的题目中,STL永远不会缺席,而在实际的开发中,STL也是贯穿整个C++项目的根脉。
网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它,许多底层的数据结构以及算法都不需要自己重新造轮子,只有站在前人的肩膀上,才能健步如飞的快速开发。
1. 5 STL的学习
以下来自侯捷老师的《STL源码剖析》:
再功利地说:在找工作之前,一定要达到第二境界,而在工作中慢慢地到达第三境界。
扩充STL不是任何人可以直接传授的道理,而是一种慢慢沉淀出来的经验,作为初学者,不要盲目地尝试,因为那大概率只是浪费时间。
1. 6 STL系列博客的规划
STL是一系列十分标准,接口命名与使用都十分规范的标准库,在本系列博客中,将以容器和配接器的使用与模拟实现为主线,在其中穿插算法,仿函数以及迭代器。
容器的接口使用其实是十分相似的,因此本系列博客会着重于容器的模拟实现,仅在string时会详细介绍几乎所有接口,其它的容器只会介绍与string差异较大的接口。
2. string
2. 1 为什么学习string类?
C语言中的字符串是以'\0'
结尾的一些字符的集合,为了操作方便,C标准库中提供了一些字符串的库函数,但是这些库函数与字符串是分离开的,不太符合OOP(面向对象程序设计)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
而string
——对字符串的封装可以很好地解决这一问题,接口会自动向字符串的尾部添加'\0'
,不用自己操心'\0'
,也不需要担心越界访问(一般情况下),这使得无论是在OJ题还是实际开发中,都很少有人会使用C语言的字符串。
2. 2 标准库中的string
string类被包含在<string>
的头文件中,同时也在std
这个命名空间中。
2. 3 基本构造
string 类的成员其实和数据结构初阶中的顺序表是基本一样的,都是一个数组(只不过在 string 类中固定为char了),有一个指针,_size
变量存储 string 中目前的数据个数,_capacity
存储 string 中指针的容量。
class string
{
private:char* _str;size_t _capacity;size_t _size;
};
2. 4 尾插与输出运算符重载
为了方便后文展示构造函数方便,先把这两个接口讲解一下。
- 尾插
在STL库中,除了栈和队列这样的只能尾插(或者说是入队列和入栈)的容器之外,所有容器的尾插都是以push_back
命名的,在 string 中就是:
void push_back (char c);
- 输出运算符(>>)重载
ostream& operator<< (ostream& os, const string& str);
string 并不是字符串,所以正常情况下不能直接通过输出运算符输出,但 string 类中对输出运算符进行了重载,使得其可以直接进行输出。
使用范例:
#include<string>
#include<iostream>
using namespace std;
int main()
{string s; //创建一个空的string实例化对象s.push_back('a');s.push_back('b');s.push_back('c');s.push_back('d');s.push_back('e');s.push_back('f');s.push_back('g');cout << s;return 0;
}
输出:
2. 5 构造函数
库函数的容器都提供了非常多种构造函数以让使用更方便,而在C++98中 string 提供了七种初始化方式,在C++11中扩充至九种,但多出来的两种,其中一种涉及右值引用,本文不作讲解,另一种这里只介绍用法不介绍原理。
- default (常用)
string();
不传任何参数的构造函数,理所应当的,创建的是一个没有存储任何数据的空 string。
用法:
#include<string>
using namespace std;
int main()
{string s;return 0;
}
这个 string 实例化对象 s 中的_size
是0,但其容量不一定是零。
- copy (常用)
string (const string& str);
拷贝构造,借助另一个 string 的实例化对象,拷贝构造出一个新的实例化对象。
#include<string>
#include<iostream>
using namespace std;
int main()
{string s;s.push_back('a');s.push_back('b');s.push_back('c');cout << s << endl;string s1(s);cout << s << endl;string s2 = s;cout << s2 << endl;return 0;
}
s1和s2在构造时都会以s为数据来源进行拷贝构造。
输出:
- from c-string(常用)
string (const char* s);
从字符串中拷贝数据进行构造。
指针 s 指向的是一个字符串,无论是常量字符串还是字符数组,都必须以'\0'
结尾才能保证正常构造。
#include<string>
#include<iostream>
using namespace std;
int main()
{char str[] = "Hello World!";string s(str);cout << str << endl << s << endl;return 0;
}
输出:
- substring
string (const string& str, size_t pos, size_t len = npos);
从 str 这个实例化对象中从 pos
位置(下标)开始拷贝走 len
个数据。
#include<string>
#include<iostream>
using namespace std;
int main()
{string s("Hello World!");string s1(s, 1, 6);cout << s1;return 0;
}
运行结果:
- from buffer
string (const char* s, size_t n);
根据字符串 s 的前 n 位构造出一个 string 实例化对象。
使用:
#include<string>
#include<iostream>
using namespace std;
int main()
{char str[] = "Hello World!";string s(str, 5);cout << s << endl;return 0;
}
输出:
- fill (常用)
string (size_t n, char c);
构造出存储了 n 个元素 c 的实例化对象。
#include<string>
#include<iostream>
using namespace std;
int main()
{string s(10, 'a');cout << s;return 0;
}
输出:
- range (常用)
template <class InputIterator>string (InputIterator first, InputIterator last);
实际上是使用迭代器来构造对象。
迭代器是STL中一个十分重要的组件,对于 string 来说,它的迭代器就是对指针的封装,需要在介绍begin()
和end()
接口之后才能使用。
- initializer_list
string (initializer_list<char> il);
这里的initializer_list
也是一种容器,关于它的具体说明会在vector
的相关博客中讲解,这里只介绍其使用:
#include<string>
#include<iostream>
using namespace std;
int main()
{string s({ '1','2','a','b' });cout << s << endl;return 0;
}
在创建实例化对象时,在对象名后面的括号中加上一对大括号,在其中写上一系列元素(注意对于 string,这里的元素必须是char
类型的),并以逗号隔开。
输出:
2. 6 赋值运算符重载
因为赋值运算符重载和构造函数重合度较高,所以放在一起讲解。
C++11中 string 有5种赋值运算符重载,因为其中一种涉及右值引用,这里不做介绍。
- 拷贝赋值
string& operator= (const string& str);
其使用和拷贝构造其实差不多,只是拷贝构造是在创建实例化对象时调用,而拷贝赋值是对一个已经实例化的对象的操作。
#include<string>
#include<iostream>
using namespace std;
int main()
{string s("hello world");string s1("x");cout << s1 << endl;s1 = s;cout << s1 << endl;return 0;
}
输出:
- c-string
string& operator= (const char* s);
和构造函数中的from c-string一样,略。
- character
string& operator= (char c);
和c string是一样的,只是从字符串变成了字符,用于一些场景下的兼容。
#include<string>
#include<iostream>
using namespace std;
int main()
{string s("Hello World!");s = 'a';cout << s;return 0;
}
输出:
- initializer_list
string& operator= (initializer_list<char> il);
和构造函数里的 initializer_list 是一样的,略。
2. 7 容量操作
容量操作指对 string 对象中的_capacity
和_size
进行访问或操作的接口。
size()
和capacity()
(重要)
这两个接口根据它们的名称就可以推断出分别是返回_size
和_capacity
的大小。
size_t size() const;
size_t capacity() const;
//注:省略了关于异常的关键字,现阶段不用在意
在一些情况下,可能需要在类外面访问 string 对象的大小和容量,比如对 string 进行下标遍历,size()
的大小就可以作为遍历次数。
不能直接访问对象中的 _size
和_capacity
,一方面是因为这是私有成员变量;另一方面,就算声明了友元函数,C++标准中并没有规定_size
和_capacity
的具体名称,因此不同编译器中的这两个变量的名称可能不同,如果直接在代码中访问成员变量,会使代码的可移植性变差。
length()
和max_size()
这两个接口几乎没有什么用武之地。
size()
与length()
方法底层实现原理完全相同,引入size()
的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
。
后者是返回 string 对象可以达到的最大长度,这是由已知的系统或库实现限制推断出的字符串可以达到的最大潜在长度,但不能保证对象能够达到该长度,在达到该长度之前,它仍然可能无法在任何时候分配存储。所以它没有意义。
- clear()
清除对象中存储的所有元素,即_size
置为0。
#include<string>
#include<iostream>
using namespace std;
int main()
{string s("Hello World!");s.clear();cout << s;return 0;
}
可以发现代码没有任何输出。
- resize()(重要)
// resize()
void resize (size_t n);
void resize (size_t n, char c);
resize()
是重新设置 string 对象的大小,如果新的_size
小于原来的,就会直接将多余元素删除(实际上就是加一个'\0'
),如果新的_size
大于原来的,会使用第二个参数 c 去填补这些位置,如果没有第二个参数,默认是'\0'
。
如果新的_size
大于原来的容量,还会进行扩容。
#include<string>
#include<iostream>
using namespace std;
int main()
{string s("Hello World!");cout << s.size() << endl;s.resize(2 * s.size());cout << s.size() << " " << s << endl;s.resize(2 * s.size(), 'a');cout << s.size() << " " << s << endl;return 0;
}
输出:
reserve()
(重要)和shrink_to_fit()
void reserve (size_t n = 0); //注意reserve()是有缺省值的!
reserve()
的作用是将 string 对象的容量扩大到n以上,具体是多少C++标准没有规定。
如果n<_capacity
,这个函数不会有任何动作。
这个接口的意义是减少扩容带来的损耗,编译器在创建一个 string 实例化对象时,并不知道它最终会存储多少数据,因此使用过程中不可避免地需要扩容,而我们知道扩容是有效率损耗的(如果原来的地址后面的空间不足,就需要拷贝原数据到新地址),因此如果程序员已知 string 对象存储的最大值或是不低于某个值,就可以直接reserve()
一下,以减少扩容。
而shrink_to_fit()
是请求字符串减小其容量以适应其大小,也就是释放没有存储数据的空间。
但是这个接口是非强制性的,C++标准只规定这个接口调用后,对象的容量不低于其大小就可以了,编译器可以在此基础上任意优化。
- empty() (重要)
返回对象是否为空(即其长度是否为 0)。
2. 8 元素访问
operator[]
重载 (重要)
string 是对字符串的封装,因此是无法直接使用下标访问操作符的,必须通过操作符重载
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
其重载的返回值均为下标指向元素的引用,只是如果对象被const
修饰不可修改,那么其返回的引用也不可修改。
下标访问操作符不会检查pos指向的位置是否合法,如果 pos 大于对象的大小,会发生未定义行为。
所有容器的元素访问操作符都会提供 const 版本以兼容对象被 const 修饰的情况。
-
at()
at()与下标访问操作符几乎一样,只是at()会检查pos位置是否合法,如果非法就会抛出异常。 -
front()
和back()
分别返回对象中第一个元素和最后一个元素的引用,注意也会有 const 修饰的区别。
2. 9 迭代器
在 string 中,可以把迭代器这样简单地理解:
typedef char* interator;
typedef const char* const_interator; //注意const迭代器的写法,用_相连
当然,现代的迭代器是对指针的进一步封装,不过在早期,string 的迭代器就是这样的,所以我们可以先这么理解,使用上是没有差别的。
- auto (重要)
auto关键字可以在创建变量自动识别其类型而无需显式写出,在迭代器这里使用的非常频繁。
对于 string 来说,它的迭代器类型为:
std::string s;
// std::string::iterator it1 = s.begin();
auto it1 = s.begin();
似乎也不是很长,但是如果是将来的一个数据结构 map,情况就不一样了:
std::map<std::string, std::string> m;
// std::map<std::string, std::string>::iterator it2 = m.begin();
auto it2 = m.begin();
不需要管这个类型具体是什么,只需要知道这样写很麻烦,而auto可以帮助我们省去这个麻烦就可以了。
比如:
补充:
- 在早期C/C++中auto的含义是使用auto修饰的变量,是具有自动存储器的局部变量,但是后来这个东西变得不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
- 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
- auto不能作为函数的参数,可以做返回值,但是建议谨慎使用 。(会降低代码可读性)
- auto不能直接用来声明数组
-
begin()
和end()
(重要)
这两个接口分别返回指向string对象第一个元素和最后一个元素的下一个位置的迭代器。
根据this指针是否被const修饰,返回的迭代器类型也会改变。
正式因为end()指向的最后一个元素的下一个位置,也就是说end()的返回值的解引用没有意义,所以在使用迭代器区间进行构造/赋值时,都是左闭右开的。 -
范围 for (重要)
如果我们要使用迭代器遍历一个string对象,原本应该是这样写的:
#include<iostream>
#include<string>
using namespace std;
int main()
{string s("Hello World!");for (auto it1 = s.begin(); it1 != s.end(); it1++){cout << *it1 << " ";}return 0;
}
当然这里有两点需要注意:
在string类中,迭代器可以直接当做指针进行解引用,也可以进行±和自增自减的操作。
迭代器在遍历时结束条件最好不要写it1 < s.end()
,因为其他容器的数据存储可能不是连续的,写成it1 != s.end()
有助于代码风格的统一。
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。
因此C++11中引入了基于范围的for循环。
for循环后的括号由冒号":
"分为两部分,第一部分是范围内用于迭代的变量,第二部分则表示被选代的范围,自动迭代,自动取数据,自动判断结束。范围for可以作用到数组和容器对象上进行遍历范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
范围for不是 string 类的特权,事实上只要类支持迭代器,且命名为 iterator 和 const_iterator ,就可以自动地支持范围for。
范围for的使用为:
#include<iostream>
#include<string>
using namespace std;
int main()
{string s("Hello World!");for (auto i : s){cout << i << " ";}return 0;
}
这里也有两点要注意:
用于迭代的变量的类型可以写成auto
,也可以显式地写出来。
如果不加&
引用符号,范围 for 在遍历时会把容器中的元素直接复制给迭代变量,迭代变量不是迭代器!
因为默认情况下会发生复制,如果迭代变量的类型不是基础变量,而是类实例化对象,可能会造成效率大幅降低,因此我们可以使用引用来规避复制:
for (auto& i : s)
{cout << i << " ";
}
但是要注意,如果容器没有被 const 修饰,那么迭代变量 i 默认是可以修改数据的,如果不希望 i 有修改的权限,可以在 auto 前加一个 const :
for (const auto& i : s)
{cout << i << " ";
}
- 反向迭代器
除了上面说的两个迭代器之外, string 还支持反向迭代器,类型为:
reverse_iterator
const_reverse_iterator
同时也有对应的rbegin()
和rend()
。
对于反向迭代器,string中最后一个数据就是开始,第一个数据是结束,因此 rbegin() 和 rend() 返回的分别是指向数据末尾和数据开始的上一个位置的反向迭代器。
反向迭代器与指针有些差异,但仍然可以进行解引用,自增自减等操作,只不过反向迭代器自增后会指向原来数据的上一个数据。
2. 10 修改
- push_back()
前面已经介绍过了。略。 - append()
C++11中,append有7中重载:
//string (1)
string& append (const string& str);
//substring (2)
string& append (const string& str, size_t subpos, size_t sublen);
//c-string (3)
string& append (const char* s);
//buffer (4)
string& append (const char* s, size_t n);
//fill (5)
string& append (size_t n, char c);
//range (6)
template <class InputIterator>string& append (InputIterator first, InputIterator last);
//initializer list(7)
string& append (initializer_list<char> il);
和构造函数有一些共通之处,就不挨个解释了。
append() 的作用就是在原来字符串的后面追加元素(如字符串等)。
operator+=
(重要!!)
append() 有很多重载,但是全部记下来似乎有些困难,但是不要紧,string 类重载了 += 运算符。
//string (1)
string& operator+= (const string& str);
//c-string (2)
string& operator+= (const char* s);
//character (3)
string& operator+= (char c);
//initializer list (4)
string& operator+= (initializer_list<char> il);
operator+= 支持 append 中除了迭代器区间,fill,substring 之外的所有操作,大部分情况下 operator+= 就可以应付了。
举例:
#include<iostream>
#include<string>
using namespace std;
int main()
{string s("Hello World!");string s1("kkkk");s1 += s;cout << s1;return 0;
}
输出:
4. assign()
为 string 对象分配一个新值,替换其当前内容。
其重载和构造函数也基本一致:
// string (1)
string& assign (const string& str);
// substring (2)
string& assign (const string& str, size_t subpos, size_t sublen);
// c-string (3)
string& assign (const char* s);
// buffer (4)
string& assign (const char* s, size_t n);
// fill (5)
string& assign (size_t n, char c);
// range (6)
template <class InputIterator>string& assign (InputIterator first, InputIterator last);
// initializer list(7)
string& assign (initializer_list<char> il);
使用起来的效果和先 clear() 再 append(xxx) 的效果是一样的。
可以当做内置类型的赋值运算符。
insert()
和erase()
C++11中,insert 可以插入的有:
// string (1)
string& insert (size_t pos, const string& str);
// substring (2)
string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
// c-string (3)
string& insert (size_t pos, const char* s);
// buffer (4)
string& insert (size_t pos, const char* s, size_t n);
// fill (5)
string& insert (size_t pos, size_t n, char c);
iterator insert (const_iterator p, size_t n, char c);
// single character (6)
iterator insert (const_iterator p, char c);
// range (7)
template <class InputIterator>iterator insert (iterator p, InputIterator first, InputIterator last);
// initializer list (8)
string& insert (const_iterator p, initializer_list<char> il);
在这些函数重载中,有的使用的是数组下标的形式,有的是迭代器,不必刻意去记,在需要的时候查一下再使用就可以了。
插入时可以看做是向这个迭代器/下标指向元素之前插入。
erase 有三个重载
// sequence (1)
// 从pos下标开始删除len个元素
string& erase (size_t pos = 0, size_t len = npos);
// character (2)
// 删除迭代器指向的元素
iterator erase (const_iterator p);
// range (3)
// 删除first-last之间的所有元素,注意左闭右开
iterator erase (const_iterator first, const_iterator last);
swap()
(重要)
用于交换两个 string 对象。
使用:
#include<iostream>
#include<string>
using namespace std;
int main()
{string s("Hello World!");string s1("kkkk");s.swap(s1);cout << s1 << " " << s;return 0;
}
需要注意的是,在算法库(<algorithm>
)中也有一个通用的swap函数:
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int main()
{string s("Hello World!");string s1("kkkk");swap(s, s1); //调用方式不一样cout << s1 << " " << s;return 0;
}
虽然这两个 swap 的结果是一样的,但是对于string来说,应该尽可能使用 string 类的成员函数中的 swap,这涉及到了深浅拷贝问题,我会在下一篇博客——string类的模拟实现中详细介绍这一点。
- replace()
这个函数使用较少,自行参考cplusplus了解一下即可。
2. 11 字符串操作
-
c_str()
(重要)
返回一个指向数组的指针,该数组包含以 null 结尾(即’\0’)的字符序列(即 C 字符串),表示 string 对象的当前值。
这个函数用于用于兼容一些string类没有实现的字符串的用法。
注意这个指针可能会在string对象调用一些接口后失效(如扩容)。 -
find()
和rfind()
find 用于在 string 对象中查找字符/字符串。
// string (1)
// 从pos位置开始查找与str相同的字符串,返回第一个元素的下标,如果没找到会返回string::npos(-1)
size_t find (const string& str, size_t pos = 0) const noexcept;
// c-string (2)
// 和上面的相同,只是从string变成了字符串
size_t find (const char* s, size_t pos = 0) const;
// buffer (3)
// 与2相同,只是只匹配s的前n个字符
size_t find (const char* s, size_t pos, size_type n) const;
// character (4)
// 从pos位置开始查找字符 c
size_t find (char c, size_t pos = 0) const noexcept;
find是从前往后查找,而rfind是从后往前查找。
e.g.使用find()找出字符串中所有’c’的下标:
#include<iostream>
#include<string>
using namespace std;
int main()
{string s("Heclcloc Wcorld!c");size_t pos = s.find('c');while (pos != string::npos){cout << pos << " ";pos++; //注意要++一次,不然会死循环pos = s.find('c', pos);}return 0;
}
find_first_of()
在字符串中搜索与其参数中指定的任何字符匹配的第一个字符。
// string (1)
// 从pos位置开始找与str中任意一个字符相同的字符
size_t find_first_of (const string& str, size_t pos = 0) const noexcept;
// c-string (2)
size_t find_first_of (const char* s, size_t pos = 0) const;
// buffer (3)
// 只匹配s的前n个字符
size_t find_first_of (const char* s, size_t pos, size_t n) const;
// character (4)
size_t find_first_of (char c, size_t pos = 0) const noexcept;
与之对应的还有一组接口:
find_first_of()
// Find character in string (public member function )
find_last_of()
// Find character in string from the end (public member function )
find_first_not_of()
// Find absence of character in string (public member function )
find_last_not_of()
// Find non-matching character in string from the end (public member function )
- compare()
比较函数。
// string (1)
//比较this和str是否相同
int compare (const string& str) const noexcept;
// substrings (2)
// 比较区间,pos,len给this,subpos和sublen给str
int compare (size_t pos, size_t len, const string& str) const;
int compare (size_t pos, size_t len, const string& str,size_t subpos, size_t sublen) const;
// c-string (3)
int compare (const char* s) const;
int compare (size_t pos, size_t len, const char* s) const;
// buffer (4)
int compare (size_t pos, size_t len, const char* s, size_t n) const;
返回值:
value | relation between compared string and comparing string |
---|---|
0 | They compare equal |
<0 | Either the value of the first character that does not match is lower in the compared string, or all compared characters match but the compared string is shorter. |
>0 | Either the value of the first character that does not match is greater in the compared string, or all compared characters match but the compared string is longer. |
2. 12 不在类中的函数重载
operator<<
和operator>>
(重要)
istream& operator>> (istream& is, string& str);
ostream& operator<< (ostream& os, const string& str);
istream
和ostream
分别是cin
和cout
的类型。
这两个重载使得 string 可以直接输入输出而不需要借助字符串。
使用:
#include<iostream>
#include<string>
using namespace std;
int main()
{string s;cin >> s;cout << s;return 0;
}
输入与输出:
- operator+
可以通过operator+=类比,只是不会修改 this 指针,而是直接返回相加后的值。
一般来讲不推荐使用,因为这个函数是传值返回,需要进行深拷贝,效率低。
getline()
(重要)
我们时常会需要输入一个带有空格的字符串,在C语言中,带有空格的字符串只能通过循环调用getchar()
来实现,但在 C++ 中提供了getline函数来解决这一问题。
istream& getline (istream& is, string& str, char delim);
istream& getline (istream& is, string& str);
istream 是 cin 的类型,第一个参数可以传 cin,第二个参数传 string 对象,第三个参数可以指定输入的停止符,默认是'\n'
,也就是在默认情况下,getline 会读取一行输入,无论是否带有空格。
使用:
#include<iostream>
#include<string>
using namespace std;
int main()
{string s;getline(cin, s);cout << s;return 0;
}
输入输出:
3. 尾声
其实对于STL来说,它们的接口并不需要死记硬背,并且大概率也背不下来。
只需要在最开始的时候了解一遍,在需要的时候如果不记得了,就可以在cplusplus上查一下,用的多了,自然而然就记住了,记不住就说明这个接口/重载不重要,不需要死磕。
谢谢你的阅读,喜欢的话来个点赞收藏评论关注吧!
我会持续更新更多优质文章
相关文章:

【C++之STL】一文学会使用 string
文章目录 1. STL导读1. 1 什么是STL1. 2 STL的版本1. 3 STL六大组件1. 4 STL的重要性1. 5 STL的学习1. 6 STL系列博客的规划 2. string2. 1 为什么学习string类?2. 2 标准库中的string2. 3 基本构造2. 4 尾插与输出运算符重载2. 5 构造函数2. 6 赋值运算符重载2. 7 容量操作2.…...

好用的办公套件--- ONLYOFFICE
目录 引言 UI界面 ONLYOFFICE 协作空间 使用协作空间三步走 一、注册与登录 二、创建房间 三、上传与编辑文档 ONLYOFFICE协作空间的安全性 ONLYOFFICE 文档 关于 ONLYOFFICE 引言 ONLYOFFICE 桌面编辑器 ONLYOFFICE是一款功能全面的办公套件,支持文档、表…...

Android View事件分发
目录 1.什么是View事件分发? 2.事件的类型 3.事件的发生 4.事件分发的方法 4.1 dispatchTouchEvent() 4.2 onTouchEvent() 4.3 onInterceptTouchEvent() 5.滑动冲突 5.1 外部拦截法 5.2内部拦截法 6.onTouch的执行高于onClick 7. onTouch()和onTouchEve…...

攻防世界GFSJ1229 Three
题目编号:GFSJ1229 解题过程 1. 附件下载是三个压缩包A.zip B.zip C.zip和一个python程序Three.py 2. A.zip可以直接解压出来,内容如下: 2022-08-27 20:16:04.246131 Func A0*X0B0 2022-08-27 20:16:05.116859 Read_Data A0.txt->A0(28829613228…...

2023 icpc杭州(M,J,D,G,H)
文章目录 [M. V-Diagram](https://codeforces.com/gym/104976/problem/M)[J. Mysterious Tree](https://codeforces.com/gym/104976/problem/J)[D.Operator Precedence](https://codeforces.com/gym/104976/problem/D)[G. Snake Move](https://codeforces.com/gym/104976/probl…...

在CentOS 7上安装Alist
在CentOS 7上安装Alist 的步骤如下: 1. 卸载旧版本 如果你之前安装过旧版本的Docker,可以先卸载它: sudo yum remove docker docker-common docker-snapshot docker-engine2. 安装依赖包 确保你的系统是最新的,并安装必要的依…...

【C/C++】memcpy函数的模拟实现
零.导言 上一篇博客我们学习了memcpy函数,不妨我们现在尝试模拟实现memcpy函数的功能。 一.实现memcpy函数的要点 memcpy函数是一种C语言内存函数,可以按字节拷贝任意类型的数组,因此我们自定义的模拟函数需要两个无类型的指针参数ÿ…...

嵌入式开发之线程互斥
目录 互斥锁初始化-pthread_mutex_init 申请锁-pthread_mutex_lock 释放锁-pthread_mutex_unlock 同步 VS 互斥 临界资源:一次只允许一个任务(进程、线程)访问的共享资源,不允许多个任务同时访问的。 临界区:访问临界区的代码 互斥机制:mutex互斥锁,任务访问临界资…...

JavaScript 变量作用域与函数调用机制:var 示例详解
JavaScript 变量作用域与函数调用机制:var 示例详解 在 JavaScript 中,作用域和闭包是理解变量生命周期和行为的核心概念。通过以下这段代码,我们将详细分析如何在不同的作用域内使用 var 关键字,并解释相关的变量访问规则 代码解…...

Linux(CentOS)安装 JDK
1、下载 JDK 官网:https://www.oracle.com/ 2、上传 JDK 文件到 CentOS,使用FinalShell远程登录工具,并且使用 root 用户登录 3、解压 JDK 创建目录 /export/server mkdir -p /export/server 解压到目录 /export/server tar -zxvf jdk-17…...

AI产品经理实战手册:策略、开发与商业化指南
通过《AI产品经理手册》,将可以了解不同类型的AI,如何将AI整合到产品或业务中,以及支持创建AI产品或将AI集成到现有产品所需的基础设施。熟悉实践管理AI产品开发流程、评估和优化AI模型,以及应对与AI产品相关的复杂伦理和法律问题…...

【大语言模型】ACL2024论文-06 探索思维链COT在多模态隐喻检测中的应用
【大语言模型】ACL2024论文-06 探索思维链COT在多模态隐喻检测中的应用 目录 文章目录 【大语言模型】ACL2024论文-06 探索思维链COT在多模态隐喻检测中的应用目录摘要研究背景问题与挑战如何解决创新点算法模型1. 知识总结模块(Knowledge Summarization Module&…...

Linux之初体验
目录 第1关:1-Linux初体验 第2关:1-Linux常用命令 第3关:1-Linux 查询命令帮助语句 第4关:2--查询命令-locate 第5关:2--查询命令-which/whereis 第6关:2--查询命令-find 第7关:3-Linux文…...

现代化水电管理:Spring Boot在大学城的实践
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...

黑马官网2024最新前端就业课V8.5笔记---HTML篇
Html 定义 HTML 超文本标记语言——HyperText Markup Language。 标签语法 标签成对出现,中间包裹内容<>里面放英文字母(标签名)结束标签比开始标签多 /拓展 : 双标签:成对出现的标签 单标签:只有开…...

GS-Blur数据集:首个基于3D场景合成的156,209对多样化真实感模糊图像数据集。
2024-10-31,由韩国首尔国立大学的研究团队创建的GS-Blur数据集,通过3D场景重建和相机视角移动合成了多样化的真实感模糊图像,为图像去模糊领域提供了一个大规模、高覆盖度的新工具,显著提升了去模糊算法在真实世界场景中的泛化能力…...

Linux下Java的多种方式安装
Linux下Java的多种方式安装 博客: www.lstar.icu 开源地址 Gitee 地址: https://gitee.com/lxwise/iris-blog_parent Github 地址: https://github.com/lxwise/iris-blog_parent 序言 Java是一门面向对象的编程语言,不仅吸收了…...

Android Studio:connect time out
参考:Android Studio:connect time out_android studio connection timed out-CSDN博客...

A014-基于Spring Boot的家电销售展示平台设计与实现
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹 赠送计算机毕业设计600…...

数学期望和联合概率密度
数学期望的定义 数学期望是描述随机变量平均趋势的一个重要统计量。根据随机变量的类型(离散或连续),数学期望的定义有所不同。 离散型随机变量的数学期望: 若离散型随机变量 X X X取值为 x 1 , x 2 , … , x n , … x_1,x_2,\do…...

萤石私有化设备视频平台EasyCVR视频融合平台如何构建农业综合监控监管系统?
现代农业的迅速发展中,集成监控管理系统已成为提高农业生产效率和优化管理的关键工具。萤石私有化设备视频平台EasyCVR,作为一个具有高度可扩展性、灵活的视频处理能力和便捷的部署方式的视频监控解决方案,为农业监控系统的建设提供了坚实的技…...

【MongoDB】Windows/Docker 下载安装,MongoDB Compass的基本使用、NoSQL、MongoDB的基础概念及基础用法(超详细)
文章目录 Windows下载MongoDB Compass使用NoSQL的基本概念MongoDB常用术语MongoDB与RDBMS区别MongoDB的CRUD 更多相关内容可查看 Docker安装MongoDB可查看:Docker-安装MongoDB Windows下载 官网下载地址:https://www.mongodb.com/try/download/communi…...

微信小程序-自定义导航栏
一.自定义导航栏 1.JSON文件中配置"navigationStyle": “custom” "navigationStyle": "custom"2.给导航栏设置轮播图 <swiper class"custom-swiper" indicator-dots autoplay interval"2000"> <swiper-item>…...

vue中强制更新视图
vue3 中强制更新视图 方式 通过 $forceUpdate 与 vue2 相似 import {getCurrentInstance} from vueconst internalInstance getCurrentInstance() //操作数据后更新视图 internalInstance.ctx.$forceUpdate()通过 key 值改变更新 <compName :key"key" />co…...

mqsql 场景函数整理
场景1:行数据取多字段,取到有值为止 解决方案: mysql coaleace函数 场景2:字符串拼接文本并换行 解决方案1: mysql concate() 和char(10) 场景3:获取单汉字首拼 解决方案1:单汉字获取首拼 解…...

【AI日记】24.11.05 向量数据库 weaviate、混合搜索、多语言搜索、明确自己的南京
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 工作1 内容:学习deeplearning.ai的课程课程:Vector Databases: from Embeddings to Applications时间:6小时评估:不错,完成收获:学…...

Scrapy入门
Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架。 安装scrapy pip install scrapy2.5.0 1.新建 Scrapy项目 scrapy startproject mySpider # 项目名为mySpider 2.进入到spiders目录 cd mySpider/mySpider/spiders 3.创建爬虫 scrapy gensp…...

Ubantu/Linux 采用Repo或Git命令报错!!
简言: 遇事还是不要慌,出现这些问题,很正常的;如果那些你不需要,只是需要回到某一个版本,那么就是需要,方法可以尝试回退节点,也可以尝试强行merge合入冲突,或找到冲突文件解决,但这些方法都非常的繁杂且不实用。以下是研究出来的解决方案! 记得随时使用git statu…...

C++简单工厂模式
什么是简单工厂模式? 简单工厂模式属于创造型模式,而工厂就是负责生产和创造的,顾名思义。建立对象的类就如一个工厂,而需要被建立的对象就是一个个产品;在工厂中加工产品,使用产品的人,不用在…...

讲讲 kafka 维护消费状态跟踪的方法?
大家好,我是锋哥。今天分享关于【讲讲 kafka 维护消费状态跟踪的方法?】面试题?希望对大家有帮助; 讲讲 kafka 维护消费状态跟踪的方法? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Kafka 中&#x…...