本文主要是分享一些基础的用法和接口,不会涉及迭代器版本,也没有底层概念,主要是保证简单入门和使用。
1.npos
string本质上是一个类,里面包含了上百个成员函数,在调用这个头文件后,我们要知道整个类都被包含在了std这个命名空间域里,因此我们要注意在大型项目中不适用using namespace std的地方时如何使用这个类。
在string里npos使用非常常见,npos是static const size_t npos = -1(大约42亿),在很多函数的参数部分作为缺省值补充
注意,这只适用于x86平台,在x64里,这个数会大很多,经过计算相当于long int的无符号最大数,占了8个字节
2.string的增删查改操作
string帮我们实现了很多的东西,让我们可以更方便地解决一些问题,将精力放在项目更关键的地方,而不是像C语言一样每个细枝末节都要去手动实现。并且,string是一个将数据和方法封装到一起的类,体现了OOP(面向对象编程)特性,而C语言中的字符串函数相比来说是数据方法分离的,并没有这一思想。
string的功能主要分为增删查改。但由于string的设计相对来说很复杂(string设计在STL之前,时间太早不太成熟,设计出现了很多冗余的接口),平时使用的时候就主要使用关键的几个,保证功能的实现即可,所以这篇文章会分享一些基础的有用的接口。
(1)增
①初始化(构造函数)
但是我们要注意的是这里面有很多的构造函数,在使用的时候是去找最匹配的。我们有时候使用的构造函数带有缺省值npos,但有的有没有,这就导致了有些函数容易造成我们理解上的歧义,需要我们着重理解。
通过分析我们发现当数据存在类型转换的时候,会去匹配最合适的函数,特别是针对字符串->类的隐式类型转换,有时候少一个参数会影响前面参数的解读,导致代码的意思完全不一样。
除此之外,我们知道有的函数进行了特殊处理,导致我们在写参数的时候可以随便写大,但有的函数就会导致越界访问,需要我们注意。
size_t len和size_t n在这里意思其实都是一样的,即从输入的str(s)中往后选取len(n)个字符构成的串初始化对象。但是区别在于len读到\0就会强制停止,而n读到\0不会停下来,会出现越界
在后面的函数中,只要是参数格式和这个相同的都会出现这种情况,需要我们多注意,否则会不经意写出很多bug。我们也可以通过其它办法进一步探究这两个函数对参数处理的区别
可以看到,在string (const char* s, size_t n);里会忽略\0的阻拦强制向后读,而string (const string& str, size_t pos, size_t len = npos);最多读到\0结束
②push_back
参数:void push_back (char c);
push_back是在已有字符串后面加上新的单字符,注意只能加一个字符,字符串不行
③append和+=
append就是追加,在参数方面和构造函数几乎一模一样
下面是用法的实例,可以稍微分析一下保证掌握:
但是,这种用法除了不方便、有时会导致歧义以外,还存在一个易错点,下面来看看这个结果:
在这种情况下,由于数据连续存储,当出现了越界访问时,会导致如上面这样的数据污染,而这样的代码并没有报错,这就很需要我们注意在追加的时候不要像构造函数那样在size_t n这个参数上随便填,最好遵循真实情况来写,我们无法保证编译器帮我们解决了这个问题。
在大部分情况下,我们遇到的追加都是整段整段的,所以在追加时我们可以直接用+=来写,方便且容易理解。
但是要注意的是+=不能对+=后跟的数据进行裁切,这是+=劣于append的一点,但绝大多数情况是够用的,如果确实要用到append,注意上面我说的要根据实际来填写参数防止越界。
下面是简单的实例:
④insert
当我们需要在字符串中间插入字符或字符串时,可以考虑使用insert
下面是一些简单使用示例:
insert要慎重使用,因为插入往往意味着要挪动数据,这会出现效率不高的问题
(2)删
①erase、clear
erase可以从指定的位置pos开始向后清除len个数据
一般来说,erase已经满足了我们的需求,掌握这一个即可,clear可以在特殊场景使用
clear:参数:void clear(); 清空字符串
②pop_back
参数:void pop_back();
删除最后一个字符
(3)查
①operator[]、at
operator[]:参数:char& operator[] (size_t pos); 针对string(可修改)
const char& operator[] (size_t pos) const; 针对const string(字符串不能修改)
这个用法很自然,没什么学习难度,但我们可以注意一下这个返回值char&,即字符串对应下标的对应数据的引用,意思就是我们直接拿到了原始数据的引用,这也就意味着除了查找以外,我们还可以用这个引用对原始的字符串进行修改操作(如++和--),我们发现这继承了数组的修改数据的用法。这个特性彻底地和指针、传值拉开了差距,使得它不可以替代
at:参数:char& at (size_t pos);
const char& at (size_t pos) const;
at和operator[]在用法和效果上完全一致,区别在于越界访问时operator[]直接assert,而at会抛异常
②size、lenth、max_size、empty
这四个函数是用来查数组的大小相关的信息
size、lenth:都是只访问不修改
参数:size_t size() const;
size_t length() const;
这两个函数的含义完全一致,就是计算字符串的大小,但是值得注意的是,lenth是单独为string这个容器设立的,而size适用于所有容器,因此一般情况下建议使用size
我们可以看到,size和lenth都不会计算\0
max_size:参数:size_t max_size() const;
用来查询字符串允许的最大长度,几乎没什么用,因为一般情况不会触及到这个最大值,下面是在x64下允许存储的最大长度,相当于有符号long int能存的最大值,需要8589934592G的空间,这是不可能达到的。
x86对应的就是有符号int的最大值,需要约2G的空间
empty:参数:bool empty() const;
用来判断字符串是否是空,在很多地方还是很有用处的
③find、rfind
当我们需要在字符串中找某个字符或字符串的时候,我们就可以考虑使用find、rfind
rfind和find在用法上完全一致,只不过rfind是从字符串后向前找,找到了就返回值。第二个参数的含义是从后向前找的第一个字符。
④c_str、data
参数:const char* c_str() const;
const char* data() const;
c_str、data用法效果完全一致
在使用string的过程中,如果我们需要用到一些C语言相关的函数,必须传址的时候,我们应该得到存这个串的地址,这个时候使用c_str即可
但是我们一定要注意这个函数const char* c_str() const;被const修饰,也就是我们不能外调函数来修改它的值,这体现的是C++的封装思想。
(4)改
①assign(覆盖)
当我们想要将整个字符串覆盖重写的话,我们可以考虑使用assign
使用的注意事项和上面的一样,掌握好这几种参数模板可以用于很多相似的函数
②replace
当我们需要将一段字符串替换成其它字符或字符串时考虑使用replace
值得注意的是第二个参数len,可以随便填大,因为删完了就不会删了,没有越界访问,相当于清除pos后面所有内容