首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C++ string类 常用的库函数 + 迭代器,范围for,auto,附使用方式

C++ string类 常用的库函数 + 迭代器,范围for,auto,附使用方式

作者头像
君辣堡
发布2025-12-20 09:11:21
发布2025-12-20 09:11:21
120
举报

这一篇我来介绍一下C++ string类中常用好用的库函数,附上使用例子:

推荐一个查C++库函数的链接:Reference - C++ Reference (cplusplus.com) 有什么需要的函数可以自己查阅

引言:

在C语言中,我们就接触过字符串,它是以\0为结尾的一些字符的集合。但在C语言使用string系列的函数比较麻烦,稍不留神还会出错。为了方便和快捷的使用,C++中就添加了string类以及相关的库函数。

下面,我将开始介绍重点的几个字符串:

1.string对象常见的构造接口

string()

构造空的string类对象,即空字符串

string(const char* s)

用C-string来构造string类对象

string(const string& s)

拷贝构造函数

使用例子:

代码语言:javascript
复制
void Teststring()
{
    string s1;                  // 构造空的string类对象s1,不需要传参。
    string s2("Hello World");     // 用C格式字符串构造string类对象s2
    string s3(s2);              // 拷贝构造s3
}

拓展:

也可以只拷贝部分字符,具体操作如下:

代码语言:javascript
复制
string s4(s2,0,5);      //会输出  "Hello"

第二个参数pos很简单,就是位置的意思,第三个参数len = npos,就有意思了:

它是string类定义的一个静态对象,const size_t类型(也就是无符号整型,x86环境下是4字节,

x64环境下是8字节)其值是-1,C语言我们学过原反补的概念, -1 代表 INT_MAX

如果给的参数错误,比如远远超过了字符串的字符数量,那么len就是npos,缺省值npos,不传参数也是npos,所以:

代码语言:javascript
复制
string(s2,6,10000);         //直接从W拷贝到结尾,"World"
string(s2,6);               //len=npos,缺省值,直接拷贝到结尾   

2.operator=

赋值运算符重载

代码语言:javascript
复制
s7 = "xxxx"           //直接用字符串赋值

如果s7是“1234567” 等,长度大于右边,也没用,直接被替换成“xxxx”

3.operator[ ]

他的底层可以理解为是这样:(这里就不深究,后面将底层原理会详细展开)

得益于这个方括号重载,我们可以让字符串和数组一样进行访问,修改:

代码语言:javascript
复制
string s1("Hello World");

s1[0] = x;                    //索引0,也就是第1个字符改成x

cout<<s1<<endl;               //  "xello World"
cout<<s1[0]<<endl;            // 'x' 

当然也可以遍历修改全部,用for循环。

注意到C++,这个重载是传引用返回,说明修改的是s1本身,可以修改返回对象

还重载了一个const重载,因为string对象有 普通对象 和 const对象。

不可以越界修改,甚至越界访问都不行,有严格的断言检查。越界就报错,并且中止程序

3.1 at

这两个函数功能一样 at函数的格式是:s1.at(12) 类似这样。

唯一不同的是,at对越界检查不是那么严格,它会抛异常,而不是直接中止程序。

了解即可。

4.string对象常见的容量操作接口

size

返回字符串有效字符长度

empty

检测字符串释放为空串,是返回true,否则返回false

clear

清空有效字符(不清除空间)

reserve

为字符串预留空间(会增容,不一定会缩容)(下方详细)

resize

将有效字符的个数改成n个,多出的空间用字符c填充

length

返回字符串有效字符长度

capacity

返回空间总大小

size和length的区别

前面5个加粗的比较重要,需要记住。 看第六个length,size和length功能和底层完全相同,名字不同。引入size()的原因是为了与其他容器的接 口保持一致,一般情况下基本都是用size()。

举例:

代码语言:javascript
复制
string s1 = "123456"
cout<<s1.length()<<endl;            //6
cout<<s1.size()<<endl;              //6  都不包含 \0

size 结合前面的 operator[ ],可以更好地做到for循环遍历+修改

代码语言:javascript
复制
for(int i = 0;i<s1.size();i++)
{
    s1[i]++;
}

clear和empty

clear是清理数据用的,但是不清理空间 使用:s1.clear();

empty就是判空,在string基本等效于if(s1=="")... 使用:if(s1.empty()) ,empty返回类型是bool

但在其他容器,用if(s1=="")... 不适用所以empty在其他容器还是有点用处的

reserve与扩容缩容机制

reserve的作用一般是确定要插入多少个字符,提前扩容:比如我知道这个s1字符串需要插入200的字符,我可以直接插入200,减少空间的浪费 使用: s1.reserve(200) 总结就是按需分配,减少性能和空间损耗。

第七个capacity:正好可以结合reserve用来讲讲扩容缩容机制。不同的编译器扩容机制可能不一样:

reserve是预留空间的函数,如果预留的还不够本身的长度,则不会预留。若预留超过了本身长度,如“hello

world” 是11个字符(空间是15),在vs下:reserve(20)(增加到20空间)就会增加到31,进行二倍扩容。在gcc中也一样,二倍扩

容(下方代码和运行结果演示:)

注意,reserve一定会扩容,但不确定是否会缩容

右边这串代码在vs和gcc,gcc会先二倍扩容,然后二倍缩容,vs则只是会扩容,不会缩容。具体取决于编译器

所以,不要尝试用reserve去缩容!

resize

用法就是这样,它会截断5后面的全部数据,所以执行resize(5)后,s3就变成 “hello” 了。这是第一个版本

第二个版本,多了一个参数char c,作用如下:

第二个版本,当大于当前数据,就会把少了的数据都填充c,没有指定c就填充空字符 \0

“hello”会变成 “helloxxxxx”

如果再接着这样:

代码语言:javascript
复制
s3.reserve(15);

结果就看似一样,没添加,但实则是没显示:

从图中可以看出,后面5个都填充了空字符 \0

5.迭代器iterator

迭代器是行为像指针一样的东西

begin+end

begin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器

begin() 返回起始位置的迭代器 end() 返回\0位置的迭代器

这 两个函数 是和 迭代器 一起用的,begin()指向第一个字符,end()指向最后一个字符的下一个字符,也就是 \0 位置,数学上

来说:

[begin,end) 这是左闭右开的区间。

使用例子:

代码语言:javascript
复制
string::iterator it1 = s1.begin();  //指向第一个字符
while(it1 != s1.end())              //不等于\0
{
    (*it1)--;                       //对迭代器解引用,获取指向的字符,对字符--
    cout<*it1<<" ";                 //打印修改后的字符,并用字符串" "(一个空格)分隔。
    ++it1;                          //迭代器自增,指向下一个字符
}

迭代器是访问所有容器的方式,在string中,下标加方括号的方式更方便,但是其他更繁琐的容器中,迭代器的作用就体现出来了。

举例一个链表的例子:

代码语言:javascript
复制
#include<list>


list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);

list<int>::iterator lit1 = lt.begin();

while(lit1 != lt.end())
{
    // (*lit1)++              //加上这个就是每个的值++,打印结果是 2 3 4
    cout<<*lit1<<' ';         //  1 2 3
    ++lit1;
}

迭代器的作用就体现出来了,链表不支持 下标+方括号 的访问。

迭代器也有 “const 迭代器” ,不过const不是修饰迭代器本身(不然迭代器指向改变不了,只能指向一个地方),而是修饰参数、(迭代器访问到的元素是const,不可修改值):

如图,有const_iterator 类型为了凸显const,名字加上了const。 end()同理,也有这个类型。

本质上这两个迭代器是两个类型了。 使用例子:

代码语言:javascript
复制
void Print(const string& s)                     //不可修改的字符串
{
    string::const_iterator it1 = s.begin();     //调用const_iterator迭代器
    while (it1 != s.end())
    {
        // *it1 = 'x';              //不可修改,因为是只读的字符串
        cout << *it1 << " ";      
        ++it1;
    }
}

5.1反向迭代器reverse_iterator

还有反向迭代器:const_reverse_iterator ,倒着遍历,同样还有rend()

rebegin() 返回指向的最后一个字符 rend() 返回指向的第一个字符的前一个,使用:

代码语言:javascript
复制
void Print(const string& s)                     //不可修改的字符串
{
    string::const_reverse_iterator it2 = s.rbegin();     
    while (it2 != s.rend())
    {          
        cout << *it2 << " ";      
        ++it2;
    }
}

为什么是++it2? 翻转后不应该是--it2吗?

因为反向迭代器,重载了++的含义,重载成了--这也是为了更便捷的使用反向迭代器。所以可不能改成“--it2”了哦

5.2迭代器使算法泛型化

像之前我们写的算法,比如查找函数 find,都是针对某一个具体容器的,比如针对数组,字符串,现在,借助迭代器,我们可以将这种算法泛型化:

这是 “算法” 的头文件,里面有很多函数,其中,实现了通用的函数 find

使用模板,利用迭代器 ,传参数就传该容器的迭代器,这样实现了find函数的泛型化

图中 第四行:first != last ,为什么不用 < ?

因为迭代器不一定支持大小比较。比如链表,是双向链表,不支持迭代器比较大小,使用 < 会直接报错!=为了提高通用性

算法泛型化,find使用例子:

有了算法泛型化,我就可以直接使用find查找各种容器,如图。

参数不局限于begin和end,也可以修改,比如:s1.begin()+3,s1.end()-1,但需要是在迭代器的基础上合法修改。

这里就使用了类模板,传参过去。

6.范围for和auto

6.1auto

auto是类型,它会通过初始化值的类型自动推导对象类型:

代码语言:javascript
复制
auto a = 3                //auto 是 int
auto b = 3.0              //auto 是 double

这是普通类型,用auto还麻烦。但是长类型中,用auto就十分方便:

代码语言:javascript
复制
//  list<int>::iterator lit1 = lt.begin();
auto lit1 = lt.begin();

auto虽然好用,但一定程度降低了代码可读性。所以,请不要auto满天飞。

auto可以推导指针:

代码语言:javascript
复制
auto p1 = &s1;
auto* p2 = &s1;

两种写法都一样,区别就是,第一个写法也可以传别的类型,第二个写法指定了一定是指针。这里可以类推引用。

代码语言:javascript
复制
int i = 3;
int& ref1 = i;
auto ref2 = ref1;

请注意,如图这样,auto不能推导出是引用,因为ref1类型是int,所以会推导出int需要指定& :auto& ref2 =ref1;

6.2范围for

直接上例子,直观理解:

代码语言:javascript
复制
for(auto ch : s1)   //字符串
{
    cout<<ch<<' ';
}

for(auto lt : lt1)  //链表
{
    cout<<lt<<' ';
}

范围for:自动取容器当中的数据(s1)赋值(给ch),自动迭代++,自动判断结束。 十分实用

不一定需要写auto,范围for不是必须结合auto。 可以写 char ch , int(若lt1是int链表),只是习惯了和auto一起用。

只需要注意别搞混: 参数中冒号左边的是右边的数据类型,而不是右边的类型。

举个例子:auto ch : s1 ,冒号右边是string类型,但其元素是char类型,所以auto推导为char,而不是string。

代码语言:javascript
复制
for(auto ch : s1)
{
    --ch;                 //修改值
}

for(auto ch : s1)
{
     cout<<ch<<' ';   
}

注意,这样是不能改变打印的值的,因为ch只是s1数据的拷贝,并不改变s1本身,第二个范围for,ch的值又被s1的数据重新覆盖了。

正确方式:

代码语言:javascript
复制
for(auto& ch : s1)
{
    --ch;                  //此时ch是引用,所以修改的是s1的数据
}

for(const auto& ch : s1)          //const auto& 更好,只读,引用减少传参
{
    cout<<ch<<' ';         //s1的数据已经被修改,打印的是第一个for循环的值
}

支持迭代器的容器,都可以用范围for,范围for的底层其实就是迭代器。数组也可以,因为数组的行为类似于迭代器,所以被特殊处理过,也可以用范围for:


7.(Modifiers)修改string对象的函数

7.1 push_back 和 append

append是追加的意思,push_back是尾插,内容比较少于是合在一起讲.

push_back只能插单个字符,不能插入字符串:

append就重载了很多个版本,可以插入字符串,字符,部分字符串,多个字符等等:

不过最重要的就 1和3插入字符串(1是string类字符串,3是字符数组,C格式字符串,都是字符串。)

举使用例子:


7.2operator+=

前面两个相比这个,可以说是一坨。

operato+= 简直就是 "21世纪最伟大的发明" ,这个十分好用,打死都得记住

它可以+= 字符串,还可以+= 字符,比前面两个方便,快捷:

s2分别+= 字符‘ ’ 和字符串“hello bit” ,简洁明了。

7.3operator+

这是operator+,相比于operator+=,功能一样齐全,用处少一点点。operator+=改变自身,所以是传引用返回。operator+不修改自身,所以是传值返回。

C++把他重载成了全局函数。 这是为什么呢?我举个例子就明白了:

目的就是让第二个写法也可以运行。因为成员函数的有this指针,左操作数必须是类对象(s3),但是全局函数没这个顾虑,可以左右互换。


7.4 insert 和 erase

有人会纳闷,为什么string类没加头插,头删,因为这两个函数就可以完成该功能。不过不太常用,效率一般比较低,不推荐用。

这是insert,主要功能是在字符串的指定位置插入字符、字符串。

代码语言:javascript
复制
//在头部插入一个字符串
s3.insert(0,"hello")

//在头部插入一个字符的写法,很鸡肋。
s3.insert(0,"x")
s3.insert(0,1,'x');    //1是个数

这是erase,主要功能就是删除。效率需要看删的位置,也不太常用

注意这个npos,不传,传太大了就是直接从pos位置直接删到结尾。


8.find

其中1和2是几乎一样的,就是在指定位置往后找字符串,找到后返回其索引,没找到返回npos。只是字符串格式不同,第三个十分不常用

第四个是重点,是在指定位置开始往后查找字符c,找到返回其索引,没找到返回npos(INT_MAX):

找字符g:没找到

找字符d:找到了 13

关于C++string的主要库函数就到这了,感谢支持。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言:
  • 1.string对象常见的构造接口
  • 2.operator=
  • 3.operator[ ]
    • 3.1 at
  • 4.string对象常见的容量操作接口
    • size和length的区别
    • clear和empty
    • reserve与扩容缩容机制
    • resize
  • 5.迭代器iterator
    • 5.1反向迭代器reverse_iterator
    • 5.2迭代器使算法泛型化
  • 6.范围for和auto
    • 6.1auto
    • 6.2范围for
  • 7.(Modifiers)修改string对象的函数
    • 7.1 push_back 和 append
    • 7.2operator+=
    • 7.3operator+
    • 7.4 insert 和 erase
  • 8.find
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档