C++ string类

news2025/2/25 9:52:14

在c语言中,我们想要记录字符串需要创建一个字符串的数组,而c++则提供了另一种方式;

也就是这篇博客所说的string类;

string类

#include<string>

作为字符串数组的升级版,string类自然也有它的独特之处——可变长数组;

 c语言中的字符串数组只能在创建的时候定好初始的长度,数据长度不能超过数组长度;

而string类则没有这样的顾虑,虽然string变量都有自己的初始长度,但是它内部的成员函数会在输入数据超过初始长度的时候,会自动开辟空间,因此可以不用考虑数组长度;

此外,c++考虑到需要兼容c语言,也给string类重载了一个类似于c语言创建字符串数组的构造方式

 但是这里有一点需要注意的是,虽然string类这里的创建方式和c语言的字符串数组一样;

但是它还是有很多不同的;

首先就是它并不是以 ‘\0’ 作为结束的标志;

我们可以看下它内部:

 可以看到 s1 并不是以 '\0’ 作为结束标志的;

好,说了那么多 string类和 c语言数组的不同,那么来讲讲string类特有的功能吧;

string类的成员函数

string类中实现了许多成员函数,并且每种函数根据不同的情况有不同的重载;

这里我们就认识一下用的比较多的函数吧;

string的构造函数

                   函数名称                         功能      
string()创造一个空的string对象
string(const char* s)用一个字符串创建string对象
string(size_t n,char c)用 n 个 c 字符创建string对象
string(const string& s)用string对象拷贝构造一个string对象

void test3()
{
	string s1;
	string s2("abcd");
	string s3(5, 'c');
	string s4(s3);

	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;
	cout << "s4 = " << s4 << endl;


}

 我们能够看到,使用不同方式都成功创建了对应的字符串;

string的容量操作函数

               函数名                      功能
size()返回字符串有效字符长度
length()返回字符串有效字符长度
capacity()返回空间总大小
empty()检测是否为空
clear()清空有效字符
reserve(size_t n = 0)为字符串开辟空间
resize(size_t n)/(size_t n ,char c)更改有效字符为n,多出的用字符c填充

c++中对于string的容量操作的函数可不少,我们先来一个一个学习吧;

size() / length() / capacity() 函数

void test4()
{
	string s1 = "hello world!";
	cout << s1.size() << endl;
	cout << s1.length() << endl;
	cout << s1.capacity() << endl;
}

首先是string的检查容量大小的函数;

我们发现 size() 函数和 length() 函数的返回值其实是一样的;

这是因为c++中,其他容器中的检查容量大小的函数都是 size() ,因此又引入了 size() 函数; 

此外,我们发现 capacity() 函数的返回值和其他两个函数不一样,这是由string类的实现形成的;

string类的实现可以看做是一个char类型的指针(实际上比这更复杂一点,但是为了好理解可以视为是char类型的指针),并且内部除了 char* 指针,还有一个 size 和 capacity 变量

而它们三者的关系类似下图: 

 

实际上string对象开辟的大小为 capacity 这么大,而内部存储的字符串大小为size大小;

这就是为什么两个函数返回值不同的原因;

而 clear() 函数和empty()函数只在乎size()的大小;

而 size 和 capacity 的大小也是有成员变量可以修改的;

reserve(size_t n) / resize(size_t n, char c) 函数

这两个函数都是针对 capacity 变量和 size 变量而出现的函数;

我们先看看 resize 函数;

void test5()
{
	string s1 = "hello world!";
	cout <<"s1.size:"<< s1.size()<<' ' <<"s1.capacity:"<<s1.capacity()<<' ' << s1 << endl;
	s1.resize(5);
	cout << "s1.size:" << s1.size() << ' ' << "s1.capacity:" << s1.capacity()<<' ' << s1 << endl;


}

 

我们发现,resize() 更改了有效字符的长度,而 capacity 并未改变;

并且就算我们将 size 改回原来的长度,也无法恢复原来的数据;

而 reserve() 函数则不用多说,自然是修改 capacity 的大小了,但是它改变得比较奇怪;

void test7()
{
	string s1 = "hello world!";
	cout << "s1.capacity : " << s1.capacity() << ' ' << s1 << endl;
	s1.reserve(20);
	cout << "s1.capacity : " << s1.capacity() << ' ' << s1 << endl;
	s1.reserve(10);
	cout << "s1.capacity : " << s1.capacity() << ' ' << s1 << endl;


}

 

 我们发现,命名我们用 reserve 函数将 capacity 更改到20,但是实际上它的大小为31;

而更改到10则没变化;

这是为什么呢?

我们先去官网上看看reserve函数的描述:

 大致意思是:

如果 n 比string 的capacity 大,那么编译器就会扩大容器的大小到 n 或者更大,而其他情况下,就根据容器的实现进行优化;

也就是说,reserve 函数并非根据你输入的数据来扩大容量,而是根据编译器内部的实现来进行扩大;

因此这就是为什么我们扩大到20的时候,会扩大到31;

而至于为什么我们想要缩小到10的时候,则没变呢?

虽然官网描述中说自由优化,实际上大部分编译器的实现当 n 小于 capacity 的时候,并不会对capacity 进行修改;

此外,官网的描述中,规定这个函数不会影响字符串的内容,因此当 n 小于 capacity 的时候就不作修改了;

string类对象的访问及遍历操作

                 函数名称                          功能
operator[] (size_t pos)返回 pos 位置的字符
begin()+end()迭代器访问
rbegin()+end()反向迭代器访问
范围forc++支持的新型for循环访问

通过之前的实践,我们都知道 string 类对象都可以直接通过 cout 直接输出;

但是c++还提供了其他方法遍历 string 类对象;

 operator[] (size_t pos)

首先就是运算符重载的 operator[] 访问了;

void test8()
{
	string s1 = "hello world!";
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i];
	}
	cout << endl;
}

 这种访问方式就和 c 语言中访问字符数组一样了;

而这样的访问方式实际上是在类内部实现的;

begin()/end()/rbegin()/rend()

c++中有各种各样的容器,由于底层结构不同,所以各种容器的访问方式也不同;

但是c++为了保证容器的统一性,而创造出了迭代器——iterator,来保证容器访问方式的一致性;

迭代器——iterator

每个容器里面都有 iterator ,它是一个类似于指针的东西,用户通过迭代器能够访问容器的内容;

每一个容器的 begin() 和 end() 函数都是为迭代器服务的

接下来我们看看迭代器的使用方式吧!

void test9()
{
	string s1 = "hello world!";
	string::iterator is1 = s1.begin();

	while (is1 != s1.end())
	{
		cout << *is1;
		is1++;
	}
	cout << endl;

}

 

 

迭代器并非通过下标访问那样,只用比较 i 和 size 之间的大小;

迭代器只能通过和容器内部的 end() 返回值来比较,看是否到达变量的尾部;

并且,迭代器也有 const 类型的迭代器,用于被 const 修饰容器变量;

void test9()
{
	const string s1 = "hello world!";
	string::const_iterator is1 = s1.begin();

	while (is1 != s1.end())
	{
		cout << *is1;
		is1++;
	}
	cout << endl;

}

 

rbegin() 和 rend()

前面说过,这两个函数属于反向迭代器,而它的用法和普通迭代器一模一样;

 

void test9()
{
	string s1 = "hello world!";
	string::reverse_iterator is1 = s1.rbegin();

	while (is1 != s1.rend())
	{
		cout << *is1;
		is1++;
	}
	cout<<endl;

}

 通过反向迭代器,我们能够反向遍历容器内容的内容,其使用方式和普通迭代器是一样的;

范围for

范围for应该算是比较通用的,就不用过多讲解了;

void test9()
{
	string s1 = "hello world!";
	string::reverse_iterator is1 = s1.rbegin();

	for (auto e : s1)
	{
		cout << e;
	}
	cout<<endl;

}

 

 

string类对象修改操作

函数名功能
push_back(char c)在字符串后面添加c
append()    //有多种重载在字符串后面追加字符串
operator+=在字符串后面追加字符串str
c_str()返回c格式字符串
find()+npos在pos位置往后找字符c,并返回所在位置
rfind()在pos位置往前找字符c,并返回所在位置
substr在 str 中从pos位置截取 n 个字符,并返回

 push_back(char c)

 这个函数一次只能在字符串尾端放一个字符;

可以直接放,也能够放char类型的变量;

void test10()
{
	string s1 = "hello world!";
	char a = 'a';
	cout << s1 << endl;
	s1.push_back('!');
	s1.push_back('!');
	s1.push_back('!');
	s1.push_back('!');
	s1.push_back(a);

	cout << s1 << endl;
}

 这里可以看到成功的在尾端放入了字符;

append(const char * s)

append(const char* s ,size_t n)

append(const string & s)

append(const string& s,size_t subpos,size_t sublen);

append(size_t n ,char c)

template<class InputIterator>
append(InputIterator first,InputIterator last)

append这个函数有比较多的重载,接下来我们看看这些重载都是怎么用的吧:

void test10()
{
	string s1 = "hello world!";
	cout << s1 << endl;
	//append(const char * s)
	s1.append("!!!");//添加!!!到s1尾部
	cout << s1 << endl;

	//append(const char* s ,size_t n)
	s1.append("++++++",5);//有6个 +  ,但只加了5个上去
	s1.append("@", 5);//n为5,但是由于字符串只有一个@,因此只加一个@
	cout << s1 << endl;


	string s2 = "how are you?";
	//append(const string & s)
	s1.append(s2);//将s2添加上去
	cout << s1 << endl;

	//append(const string& s,size_t subpos,size_t sublen);
	s1.append(s2, 4,3);//在 s2 中从 subpos位置开始添加sublen长度的字符串到s1中
	cout << s1 << endl;

	//append(size_t n ,char c)
	s1.append(5, '*');//添加n个c到s1中
	cout << s1 << endl;

	//template<class InputIterator>
	//append(InputIterator first,InputIterator last)
	string s3 = "goodbye!";
	s1.append(s3.begin(), s3.begin() + 3);//将s3中从begin()位置开始到begin()+3位置的字符添加到s1中
	cout << s1 << endl;

}

 

 用法如上

operator+= 

相比于 append ,+= 的重载就比较少;

它的用法就是单纯的将 += 右边的字符串添加到左边字符串的尾端;

也是比较常用的一种接口; 

void test11()
{
	string s1 = "hello world!";
	s1 += "!!";
	cout << s1 << endl;

	string s2 = "how are you?";
	s1 += s2;
	cout << s1 << endl;

	s1 += '#';
	cout << s1 << endl;
}

 

 

const char* c_str() const

c_str()是一个普通的函数,就是返回string对象中的c字符串;

void test11()
{
	string s1 = "hello world!";
	cout << s1.c_str() << endl;


}

 

find(const string& s,size_t pos = 0)

find(const char* s,size_t pos = 0)

find(const char* s,size_t pos,size_t n)

find(char c,size_t pos = 0)

该函数就是在对象找寻找对应字符的位置,并且返回,若是没找到就会返回-1;

void test11()
{
	string s1 = "hello world!";
	string s2 = " wo";

	//find(const string& s,size_t pos = 0)
	int pos = s1.find(s2);//默认从0位置开始找
	cout << pos << endl;

	pos = s1.find(s2, 3);//从3位置开始找
	cout << pos << endl;
	
	//find(const char* s,size_t pos = 0)
	pos = s1.find("llo", 0);//从0位置开始找
	cout << pos << endl;

	//find(const char* s,size_t pos,size_t n)
	pos = s1.find("ld!",0,5);//从0位置开始找,只找5个位置
	cout << pos << endl;

	//find(char c,size_t pos = 0)
	pos = s1.find('d', 0);//从0位置开始找
	cout << pos << endl;
}

 

rfind(const string& s,size_t pos = npos)

rfind(const char* s,size_t pos = npos)

rfind(const char* s,size_t pos,size_t n)

rfind(char c,size_t pos = npos)

rfind 和 find 的使用方式一样,只不过是向前寻找,就不做多解释了;

而上面的 npos 实际上就是表示 string 字符串的尾部,因为 rfind 是向前寻找,因此缺省值给的尾部值;

substr(size_t pos = 0,size_t  len = npos)

void test12()
{
	string s1 = "hello world!";
	cout<<s1.substr(0,5)<<endl;
}

 

 

这个函数就是在字符串中从 pos 位置截取 len 长度的字符并返回;

不过原字符串不会消失;

string的其他函数

函数名功能
operator+将两个string或者字符串加起来并返回
operator>>整体输入string类
operator<<整体输出string类
getline()以行接收string类
relational_operators比较大小

operator+ 

 

void test13()
{
	string s1 = "hello world!";

	cout << s1 << endl;

	s1 = s1 + "!!!";
	
	cout << s1 << endl;

}

 

 这是一个运算符重载,返回一个 + 左边的字符串在前,右边的字符串在后组合而成的字符串;

当然,它也可以多个字符串相加;

void test13()
{
	string s1 = "hello world!";

	cout << s1 << endl;

	s1 = s1 + "!!!";
	
	cout << s1 << endl;

	s1 = "!!!" + s1 + "!!!";
	cout << s1 << endl;

}

 operator>> / operator<<

这两个其实在前面就已经用过多次了:

void test13()
{
	string s1;
	cin >> s1;
	cout << s1;

}

 

其中的 operator>> 和普通的输入一样,遇见空格或者换行就会中断,如上图;

getline(istream& in,string& s,char delim)

getline(istream& in,string& s)

 为了能够按行输入string,c++有 getline 函数;

一种形式是遇到和 delim 相同的字符就停止

void test13()
{
	string s1;
	//getline(istream& in,string& s,char delim)
	getline(cin, s1,'o');//按行接收字符串,遇到delim就结束,这里遇到o就不接收
	cout << s1 << endl;

}

 

一种解释单纯的按行接收

void test13()
{
	string s1;
	//getline(istream& in,string& s)
	getline(cin, s1);//按行接收字符串
	cout << s1 << endl;

}

 

 以上就是string类常用的几种函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/151562.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动驾驶标定基础知识

目录基础概念1. 缩略语2. 为什么需要外参标定3. 基于使用场景的标定分类4. 基于方法的分类5. 基础坐标系6. 超差EOL标定1.EOL特点2. EOL标定流程3. EOL标定软件约束4. EOL标定软件流程5. 算法设计原则6. 算法基本原理背景式标定1.背景式标定的特点2. 背景式标定运行流程3. 背景…

高级树结构之线索化二叉树

文章目录一 线索化二叉树简介二 线索化规则三 前序线索化3.1 代码演示四 中序线索化4.1 代码演示五 后序线索化5.1 代码演示一 线索化二叉树简介 线索化&#xff1a;将一颗二叉树的结点指向为空的指针&#xff0c;线索化为某一种顺序遍历的的指向下一个按顺序的结点的指针一颗…

虚拟主机3种方式nginx/apache+跨域知识点整理

目录referer、prototype、array、json笔记整理: [http://t.csdn.cn/s4P8x](http://t.csdn.cn/s4P8x)虚拟主机3种方式nginx/apache跨域知识点整理一、Apache基于多IP、多端口、多域名访问1、添加网卡三种方法1、虚拟机添加网络适配器2、命令添加3、用nmtui 添加ip地址2、添加配置…

Jwt 最流行的跨域身份验证解决方案

1. 什么是JWT JSON Web Token (JWT)是一个开放标准(RFC 7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任&#xff0c;因为它是数字签名的。 JWT被设计为紧凑且安全的&#xff0c;特别适用于分…

Python制做一个电脑通知小工具,再也不怕忘记事情拉~

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ Windows不是有个消息通知功能&#xff0c;挺喜欢这个功能的&#xff0c; 但是不太方便使用&#xff0c;也懒得去研究&#xff0c; 于是准备用Python自己写一个&#xff0c;通过设定通知的间隔时…

Pikachu靶场暴力破解通关

目录 字典获取 BP四种攻击模式 一、Sniper(狙击手模式) 二、Battering ram(攻城锤模式) 三、Pitchfork(叉子模式) 四、Cluster bomb(炸弹模式) 靶场练习 基于表单的暴力破解 验证码绕过(on server) 验证码绕过(on client) token防爆破? 字典获取 在github里找到心…

公司注册商标的流程是什么

公司商标注册流程是什么? 1、商标查询。查询有关商标登记注册情况&#xff0c;以了解自己准备申请注册的商标是否与他人已经注册或正在注册的商标相同或近似的程序; 2、申请文件准备。商标注册申请书;)商标图样;申请注册集体商标、证明商标的&#xff0c;应当提交申请人主体资…

发布了一个jar包到中央仓库,我的心好累…

原创&#xff1a;微信公众号 码农参上&#xff0c;欢迎分享&#xff0c;转载请保留出处。 哈喽大家好啊&#xff0c;我是Hydra。 前几天我在网上冲浪的时候&#xff0c;看见有一个老铁在git上给我提了一个issue&#xff1a; 万万没想到&#xff0c;有一天我写的烂代码居然也会…

家庭装修流程五大步骤是怎样的,家庭装修有什么注意事项!

家庭装修流程五大步骤是怎样的家庭装修有什么注意事项&#xff01;现在的人们不是在城里面购买了楼房&#xff0c;就是在家里面进行翻修房子&#xff0c;不管是哪一种&#xff0c;都肯定是需要用到装修的。但是有很多的人们对于装修方面并不是很了解。想知道家庭装修流程五大步…

Qt之对话框(QDialog)

文章目录一、对话框的概念二、与QWidget的区别三、对话框2种显示方法四、对话框返回值的概念本节示例提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、对话框的概念 对话框是和用户简短交互的一种窗口。如&#xff1a;登录界面&#xff0c;关于界面…

性能-可观测数据、量化指标、性能问题、压测目标

可观测的数据数据指标 服务应用&#xff1a;CPU利用率、内存、ygc次数接口&#xff1a;核心关注接口的请求响应时间&#xff08;平均响应时间、P90、P95&#xff0c;核心关注后面2个&#xff09;、慢请求情况数据库&#xff1a;主要关注CPU使用率、内存使用率Redis&#xff1a…

前端组件库自定义主题切换探索-02-webpack-theme-color-replacer webpack 的实现逻辑和原理-01

本文来研究写webpack-theme-color-replacer webpack 的实现逻辑和原理。 上一篇我们讲过&#xff0c; webpack-theme-color-replacer webpack 基本思路就是&#xff0c;webpack构建时&#xff0c;在emit事件&#xff08;准备写入dist结果文件时&#xff09;中&#xff0c;将即将…

Linux ALSA 之五 ALSA Proc Info

ALSA Proc Info一、概述二、Proc Files of Alsa Driver1、/proc/asound/xxx 简述2、创建 /proc/asound 目录树2.1 /proc/asound/version 文件2.2 /proc/asound/devices 文件2.3 /proc/asound/cards 文件2.4 /proc/asound/cardx 目录2.5 /proc/asound/pcm 文件一、概述 Linux系…

以“辛”为鉴,直播电商如何“知兴替”?

直播电商行业近年来创造了一个又一个销售神话。辛选2022年双11期间&#xff0c;18场直播销售额破亿。罗永浩在淘宝的首秀&#xff0c;6小时带货2亿元。直播间内外似乎一片繁华&#xff0c;但与此同时&#xff0c;也有许多曾经创造了带货奇迹的主播被市场淘汰&#xff0c;淡出视…

【笔记:第2课】学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春

文章目录前言来源正文小结前言 创作开始时间&#xff1a;2023年1月9日20:11:06 如题&#xff0c;学习一下RISC-V。 来源 https://www.bilibili.com/video/BV1Q5411w7z5?p2&vd_source73a25632b4f745be6bbcfe3c82bb7ec0 正文 计算机硬件组成&#xff1a; 总线CPU&…

迷宫问题 | 深度优先

目录 一、说明 二、步骤 三、代码 四、结果 一、说明 什么是深度优先&#xff1f; DFS即Depth First Search&#xff0c;深度优先搜索属于图算法的一种&#xff0c;是一个针对图和树的遍历算法&#xff0c;利用深度优先搜索算法可以产生目标图的相应拓扑排序表&#xff0c…

身兼数据科学家和自由职业者,算算我在2022赚了多少钱?

2022年,我作为自由职业者数据科学家赚了多少钱&#xff1f;长按关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩扫码关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩大家好&#xff0c;首先&#xff0c;我已经等了很久了。2…

保姆级 | 最新Burpsuite安装配置

文章目录 0x00 前言 0x01 环境说明 0x02 准备工作 0x03 安装JDK 0x04 配置JDK环境 0x05 Burpsuite安装 0x06 Burpsuite环境配置 0x07 Burpsuite设置代理 0x08 Burpsuite使用验证 0x09 总结 0x00 前言 Burp Suite 是用于攻击 web 应用程序的集成平台&#xff0c;包含了…

mongodb 中做 join 的方法

【问题】Imagine you have a collection for posts, and each of these posts has the attribute userid: ObjectId( ), where ObjectID is referencing a document in the Users collection.How would you go about retrieving the user information (in this case, the user …

GC耗时高,原因竟是服务流量小?

简介 最近&#xff0c;我们系统配置了GC耗时的监控&#xff0c;但配置上之后&#xff0c;系统会偶尔出现GC耗时大于1s的报警&#xff0c;排查花了一些力气&#xff0c;故在这里分享下。 发现问题 我们系统分多个环境部署&#xff0c;出现GC长耗时的是俄罗斯环境&#xff0c;…