C++STL的string类(一)

news2025/1/10 23:57:09

文章目录

  • 前言
    • C语言的字符串
  • string
  • string类的常用接口
    • string类的常见构造
      • string (const string& str);
      • string (const string& str, size_t pos, size_t len = npos);
    • capacity
      • size和length
      • reserve
      • resize
        • resize可以删除数据
    • modify
      • 尾插
        • 插入字符
        • 插入字符串
      • insert
      • erase
      • replace
    • 迭代器
      • swap
      • c_str
      • find
    • 反向迭代器
    • const迭代器
    • auto

前言

无论c++还是c语言,字符串都是最常见的类之一。我们日常当中写的程序必然要存储数据。
我们的内置类型只能表示基础的信息,无法表示一些复杂的信息,比如int, double.但我们要表示身份证、住址那就表示不了了。

C语言的字符串

C语言用字符数组来表示字符串,但是这里有一个巨大的缺陷。

1.不够好用
2.不能够很好的管理
比如:用字符数组来存储地址,但是地址的长度要修改呢?这就很麻烦。

所以c++提供了一个管理字符串的一个类,string,你可以把它想象成存储字符的顺序表。

string

string是一个类模板,它是typedef出来的。
在这里插入图片描述

string的底层是一个字符数组,但是你可以把它想象成可以增删查改的数组。

string类的常用接口

string类的常见构造

string (const string& str);

#include <string>
int main()
{
	string s2("hello world");
	for (size_t i = 0; i < s2.size(); ++i)
	{
		s2[i]++;
	}

	cout << s2 << endl;

	for (size_t i = 0; i < s2.size(); ++i)
	{
		cout << s2[i] << " ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

为什么可以这样构造?

string s1="hello world";

本质是类型转换,把const char* 转换成string.
它是先构造再拷贝构造,然后优化成了构造。

string (const string& str, size_t pos, size_t len = npos);

int main()
{
	string s3 = "hello world";
	string s4(s3, 6, 3);
	cout << s4 << endl;
	
	string s5(s3, 6, 13);//如果字符不够,有多少取多好
	cout << s5 << endl;
	return 0;
}

在这里插入图片描述
** 如果第三个参数不给呢?**

string s6(s3, 6);

这里给了一个参数,npos,并且npos=-1;
-1代表什么,这里其实是无符号,所以-1表示42亿九千万。
npos很大意味着取到结束

capacity

容量没有把’\0’算进去。

size和length

size和length有什么差异?

int main()
{
	string s3 = "hello world";
	cout << s3.size() << endl;
	cout << s3.length() << endl;
	return 0;
}

没有什么差异。

** 那为什么同时会有这两个东西呢?**
跟STL的发展历史有关。平时用size就可以了。

reserve

** 观察vs编译器的扩容情况**
在这里插入图片描述

** 那假如我知道要开多少空间呢?**
我们可以调用这样一个接口,reserve
知道需要开多少空间,提前开空间,减少扩容,提高效率。

s.reserve(100)

注意,你要100的空间,它不一定给你100的空间,它可能为了一些对齐等等的原因,可能开的比100大一些。

resize

resize和reserve功能相似,但也有很大区别。
resize除了开空间它还帮助初始化

int main()
{
	string s1;
	s1.reserve(100);
	cout << sizeof(s1) << endl;
	cout << s1.size() << endl;
	cout << "--------------" << endl;
	string s2;
	s2.resize(100);
	cout << sizeof(s2) << endl;
	cout << s2.size() << endl;
	return 0;
}

在这里插入图片描述

那初始化填了什么值呢?
填的是0;

那我想填其他的值怎么办呢?
比size小,删除数据,保留前5个

s2.resize(100, 'x');

但是它不会缩容。

这里提一个点,为什么编译器不会轻易的缩容?
缩容其实是不支持原地缩的,原地缩荣的话,那以为这要delete一部分空间,这增加了内存管理的难度。
真正缩荣都是开好另一块空间,然后将需要保留的数据拷贝过去。这也意味着缩容肯定会带来性能上的消耗。一般来说不要轻易的缩容。

resize可以删除数据
s2.resize(5);

modify

string 最好用的地方就是不用去管空间。

尾插

插入字符
int main()
{
	string s3 = "hello world";
	s3.push_back(' ');
	s3.push_back('!');
	return 0;
}
插入字符串
s3.append("bit");

但是我们不管是插入字符还是插入字符串都不喜欢这样写,我们喜欢用运算符重载+=;

s3+=' ';
s3+='!';

s3+='bit';

不过+=底层还是调用了push_back和append.

insert

如果我们再头部或者中间插入一个数据,我们就可以用insert;

int main()
{
	string s1("world");
	s1.insert(0, "hello");
	cout << s1 << endl;
	return 0;
}

在这里插入图片描述

中间插入

int main()
{
	string s1("world");
	s1.insert(0, "hello");

	//s1.insert(5, 1, ' ');
	//s1.insert(5, " ");
	s1.insert(s1.begin() + 5, ' ');//迭代器位置
	cout << s1 << endl;
	return 0;
}

在这里插入图片描述

不推荐经常使用,能不用就不用。因为要挪动数据,影响性能。

erase

删除一个字符

int main()
{
	string s2("hello world");
	//s2.erase(5, 1);
	s2.erase(s2.begin() + 5);//迭代器位置
	cout << s2 << endl;
	return 0;
}

在这里插入图片描述

删除多个字符

int main()
{
	string s2("hello world");
	//s2.erase(5, 30);//如果要删除的数据大于字符串的剩余长度,那就相当于全部删完
	s2.erase(5);
	cout << s2 << endl;
	return 0;
}

在这里插入图片描述

不推荐经常使用,能不用就不用。因为要挪动数据,影响性能。

replace

将hello world 中间空格,替换成%%d

string s1("hello world");
s1.replace(5, 1, "%%d");

replace 能不用就不用,为什么?

1.空间不够就要扩容
2.需要挪动数据

有个题目,把hello world i love you 中的所有空格替换成%20

int main()
{
	string s1("hello world i love you");
	size_t pos = s1.find(' ');
	while (pos != string::npos)
	{
		s1.replace(pos, 1,"%20");
		 pos = s1.find(' ');
	}
	cout << s1 << endl;
	return 0;
}

在这里插入图片描述

那上面的代码能不能优化一下呢?
每次都是从0的位置开始找,其实没必要。
还有一个点就是,replace可能会扩容。

int main()
{
	string s1("hello world i love you");
	size_t num = 0;
	//计算有多少个' ',为开空间做准备
	for (auto ch : s1)
	{
		if (ch == ' ')
			++num;
	}
	// 提前开空间,避免repalce时扩容
	s1.reserve(s1.size() + 2 * num);

	size_t pos = s1.find(' ');
	while (pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
		pos = s1.find(' ', pos + 3);
	}
	cout << s1 << endl;
	return 0;
}

再给大家看个好玩的东西

int main()
{
	string s1("hello world i love you");
		string newStr;
		size_t num = 0;
		for (auto ch : s1)
		{
			if (ch == ' ')
				++num;
		}
		// 提前开空间,避免repalce时扩容
		newStr.reserve(s1.size() + 2 * num);
	
		for (auto ch : s1)
		{
			if (ch != ' ')
				newStr += ch;
			else
				newStr += "%20";
		}
	
		s1 = newStr;
		cout << newStr << endl;
	return 0;
}

在这里插入图片描述

这个是以空间换时间的方式,不需要挪动数据。

迭代器

如果不用[]加下标怎么访问string对象呢,这里要用到迭代器。

int main()
{
	string s1("hello world");
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

这个代码看起来有点懵,可以暂时先把迭代器理解为指针。
在这里插入图片描述

bein()表示第一个字符的地址,end()表示最后一个字符下一个位置的地址。
它是左闭右开。

其实还可以用范围for来访问,不过范围for的底层原理还是迭代器

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

swap

看下面的代码有什么区别?

int main()
{
	string s1("hello world");
	string s2("xxxxx");
	s1.swap(s2);
	cout << s1 << endl;
	cout << s2 << endl;
	cout << "------------" << endl;
	swap(s1, s2);
	cout << s1 << endl;
	cout << s2 << endl;
	return 0;
}

s1.swap()和swap()有什么区别?
我们知道swap()是类模板,所有类型都可以交换,是泛型模板。

s1.swap()和swap()谁的效率高?
很明显s1.swap()的效率更高,s1和s2两段空间,只需要交换两段空间指针的指向就可以了。
而swap()要产生一个临时对象,需要调用拷贝构造,还是深拷贝,然后又需要两次赋值。

c_str

int main()
{
	string s1("hello world");
	cout << s1 << endl;
	cout << s1.c_str() << endl;
	return 0;
}

两者都可以打印数据,那它们的区别是什么?
s1.c_str()是遇到‘\0’结束,而cout << s1 << endl;则是根据s1.size()来打印的。

c_str的主要作用还是,为c的接口提供兼容

find

find其实前面已经见过了,再看一个例子,怎样取文件名的后缀。

int main()
{
	string file("string.cpp");
	size_t pos = file.find('.');
	if (pos != string::npos)//npos是静态成员变量,所以可以这样写,直接加上类域
	{
		string suffix = file.substr(pos, file.size() - pos);//substr表示从某个位置开始,取len字符长度的字符串
		string suffix = file.substr(pos);
		cout << suffix << endl;
	}
	return 0;
}

在这里插入图片描述

如果文件有多个点怎么样找后缀?
倒着找

size_t pos = file.rfind('.');

反向迭代器

除了正着访问string,还可以反着访问,这里要用到反向迭代器。

int main()
{
	string s1("hello world");
	string::reverse_iterator rit = s1.rbegin();
	while (rit != s1.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	return 0;
}

在这里插入图片描述

在这里插入图片描述

const迭代器

写成这样为什么会报错?

void Func(const string& s)
{
	// 遍历和读容器的数据,不能写
	string::iterator it = s.begin();//报错这里赋值不过去
	while (it != s.end())
	{
		//*it += 1;
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
int main()
{	
	string s1("hello world");
	Func(s1);
	return 0;
}

在这里插入图片描述
有两个版本,const对象返回const对象,普通对象返回普通对象。
所以很显然,应该改成const迭代器

string::const_iterator it = s.begin();//只能遍历和读取容器的数据,不能写

为什么要有返回const的迭代器呢?
不允许被修改。

普通迭代器和 const迭代器的区别?
能不能写的问题。

auto

正向的普通迭代器和const迭代器一共就有4种,我们可以用auto来优化一下迭代器的写法。

auto it = s.begin();

不过它也有一个弊端,降低了程序的可读性。

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

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

相关文章

解决WPS拖动整行的操作

如上图&#xff0c;想要把第4行的整行内容&#xff0c;平移到第1行。 1.选中第4行的整行 2.鼠标出现如图的样子时&#xff0c;按住鼠标左键&#xff0c;上移到第1行位置后&#xff0c;放开左键即可。

PPT设置章节

0 Preface/Foreward 1 添加章节方法 选择 > 开始 > 节 可以进行&#xff1a; 新增节重命名节删除所有节 相关节的内容如下&#xff1a;

vivado时序方法检查5

TIMING-14 &#xff1a; 时钟树上的 LUT 在时钟树上发现 LUT <cell_name> 。不建议在时钟路径上包含 LUT 单元。 描述 时钟路径上的 LUT 可能导致偏差过大 &#xff0c; 因为时钟必须在穿过互连结构的常规布线资源上进行布线。除偏差过大外 &#xff0c; 这些路径更…

【Java系列】函数式接口编程

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【算法每日一练]-结构优化(保姆级教程 篇4 树状数组,线段树,分块模板篇)

除了基础的前缀和&#xff0c;后面还有树状数组&#xff0c;线段树&#xff0c;分块的结构优化。 目录 分块 分块算法步骤&#xff1a; 树状数组 树状数组步骤&#xff1a; 线段树点更新 点更新步骤&#xff1a; 线段树区间更新 区间更新步骤&#xff1a; 分块 分块算…

Linux CentOS本地部署SQL Server数据库结合cpolar内网穿透实现公网访问

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;数据结构、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 安装sql server二. 局域网测试连接三. 安装cpolar内网穿透四. 将sqlserver映射…

集合进阶指南:从基础知识到高级应用

集合高级 Collection集合 数组和集合的区别 相同点 都是容器,可以存储多个数据 不同点 数组的长度是不可变的,集合的长度是可变的数组可以存储基本数据类型和引用数据类型 集合只能存储引用数据类型,如果要存基本数据类型,需要存对应的包装类 集合类体系结构 Collectio…

一天一个设计模式---原型模式

基本概念 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;其主要目的是通过复制现有对象来创建新对象&#xff0c;而不是通过实例化类。原型模式允许在运行时动态创建对象&#xff0c;同时避免了耦合与子类化。 在原型模式中&#xff0…

Tap虚拟网卡

1 概述 Tap设备通常用于虚拟化场景下&#xff0c;其驱动代码位于drivers/net/tun.c&#xff0c;tap与tun复用大部分代码&#xff0c; 注&#xff1a;drivers/net/tap.c并不是tap设备的代码&#xff0c;而是macvtap和ipvtap&#xff1b; 下文中&#xff0c;我们统一称tap&#…

C++ vector基本操作

目录 一、介绍 二、定义 三、迭代器 四、容量操作 1、size 2、capacity 3、empty 4、resize 5、reserve 总结&#xff08;扩容机制&#xff09; 五、增删查改 1、push_back & pop_back 2、find 3、insert 4、erase 5、swap 6、operator[] 一、介绍 vector…

圣诞将至—C语言圣诞树代码来啦

文章目录 圣诞将至—C实现语言圣诞树源码 圣诞将至—C实现语言圣诞树 圣诞树 源码 #define _CRT_SECURE_NO_WARNINGS#include <stdio.h> #include <math.h> #include <stdlib.h> #include <windows.h> #include <time.h> #define PI 3.14159265…

Maven-高效的Java项目构建与管理工具(含Maven详细安装与配置过程)

Maven 什么是Maven&#xff1f; 正如题目所说&#xff0c;Maven就是一款高效的Java项目构建与管理工具&#xff0c;基于项目对象模型&#xff08;POM&#xff09;概念&#xff0c;利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。是Apache软件基金会的一个开源…

全球市场调研:找准热门产品,开创跨境电商新蓝海

在全球数字化浪潮的推动下&#xff0c;跨境电商正蓬勃发展&#xff0c;成为连接世界各地消费者与商品的桥梁。然而&#xff0c;在竞争激烈的市场中要想脱颖而出&#xff0c;关键在于深入的全球市场调研。本文将探讨如何通过全球市场调研找准热门产品&#xff0c;开创跨境电商的…

如何使用Matlab完成窗口与子窗口

目录 一、前言 二、主窗口与主窗口按钮 三、子窗口 四、调用函数并显示在子窗口中的文本框中 五、关闭子窗口 一、前言 有时候需要借用Matlab完成一个图窗功能&#xff0c;但是我们的程序不仅拥有功能&#xff0c;还拥有一些子功能&#xff0c;那么我们该如何借助Matlab完…

基于OpenCV+CNN+IOT+微信小程序智能果实采摘指导系统——深度学习算法应用(含pytho、JS工程源码)+数据集+模型(四)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境TensorFlow 环境Jupyter Notebook环境Pycharm 环境微信开发者工具OneNET云平台 模块实现1. 数据预处理2. 创建模型并编译3. 模型训练及保存1&#xff09;模型训练2&#xff09;模型保存 4. 上传结果1&#xff09;…

[TKDE2020]@Multi-Source_Spatial_Entity_Linkage

论文地址&#xff1a;https://arxiv.org/pdf/1911.09016v1.pdf&#xff08;下文中提及的引用信息如未解释&#xff0c;请索引原论文末的参考文献&#xff09; 论文中提到的SSTD2019Multi-Source Spatial Entity Linkage (提取码&#xff1a;i3xt) 论文重要部分翻译 Abstract …

LeedCode刷题---双指针问题(二)

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C/C》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、盛水最多的容器 题目链接&#xff1a;盛最多水的容器 题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xf…

C#大型LIS检验信息系统项目源码

LIS系统&#xff0c;一套医院检验科信息系统。它是以数据库为核心&#xff0c;将实验仪器与电脑连接成网&#xff0c;基础功能包括病人样本登录、实验数据存取、报告审核、打印分发等。除基础功能外&#xff0c;实验数据统计分析、质量控制管理、人员权限管理、试剂出入库等功能…

vscode eide arm-gcc 编译环境搭建调试

安装cube&#xff0c;vscode 1.安装vscode插件 C/C Extension Pack Chinese (Simplified) (简体中文) Language Pack Cortex-Debug Embedded IDE 工具链设置 2.软件工程生成 调试 3.生成工程&#xff0c;导入工程 4. 配置工程 编译完毕

【Git】本地代码如何托管到远程仓库(保姆级教程)

注意前提是你本地已经安装了Git 1. 新建远程仓库 选一个git服务器&#xff0c;新建一个远程仓库&#xff0c;这里我选用的是gitee 点击确定后&#xff0c;显示如下界面表示仓库已经新建完成 2.建立本地仓库 本地新建一个空文件夹&#xff0c;在里面写一些你的初始代码文件。…