【C++】深入理解string类

news2024/12/23 23:55:40

一、熟悉string类

1.1 string类的由来:

        C语音中的字符串需要我们自己管理底层空间,容易内存泄露。而C++是面向对象语音,所以它把字符串封装成一个string类。

        C++中对于string的定义为:typedef basic_string string; 也就是说C++中的string类是一个泛型类,由模板而实例化的一个标准类,本质上不是一个标准数据类型。

至于我们为什么不直接用String标准数据类型而用类是因为一个叫做编码的东西

        我们每个国家的语言不同 比如说英语使用26个英文字母基本就能表述所有的单词 但是对于中文的字符呢?是不是就要用其他编码方式啊(比如说UTF-8或者GB2312)

1.2  string类的函数接口:


二、string类的写入:

2.1  string类的构造函数:

 我们一般都是用无参的构造,拷贝构造和有参数的构造。

string s1;

string s2(s1);

string s3("hello C++");

string s4="hello C++";//s3 s4都是会把"hello C++"字符数组隐式转换成const char*类型,然后再调用这个构造函数,对s4进行构造

不需要担心空间不够问题,这些构造函数内部都会实现自动扩容的操作。

2.2  string类的数据读取:

方法一:string类里面对运算符[ ] 进行了重载,可以像使用数组一样使用。

for(int i=0;i<s1.size(),i++)
{
    cout<<s1[i]<<" ";
}

这里的s1.size()是统计字符串长度(不包括\0)。如hello world的size是11 

方法二:调用at函数

for(int i=0;i<s1.size(),i++)
{
    cout<<s1.at(i)<<" ";
}

operator[ ]和at两种方法的区别:

    下标访问报断言错误 at访问报异常

方法三:迭代器的方法。

迭代器的写法:首先写个string类名 后面跟上iterator(迭代器) 再后面加上一个it变量(可以是其他名字)

只时候就体现了auto的好处了  auto it = s1.begin(); //自动转换类型

string::iterator it=s1.begin();
while(it!=s1.end)
{
    cout<<*it<<" ";
    it++;
}

 注意点:1.   begin是指向开始位置,而end是指向\0位置---->左闭右开

                2.不要写成it<s1.end,因为物理空间中不一定begin的就比end的小,如链表,树等是靠指针指向下一个节点。(而且底层不一定就是指针,这个后面讲)

                3.为啥需要迭代器?统一操作,关心效果,不关心底层原理(C++面向对象思维)

这个代码的意思是,it指向字符串的第一个元素

      (目前阶段可以理解为it就是一个指针)

      (s1.begin()就是指向字符串第一个字符的指针)

在循环当中,如果it不等于最后一个字符所在的位置,就打印并且it++; 

 迭代器包括正向迭代器和反向迭代器,还有有无const修饰的迭代器

这里简单介绍一下 反向迭代器 const修饰的反向迭代器

string::reverse_iterator it = s1.rbegin();
while(it!=s1.rend)
{
    cout<<*it<<' ';
    it++;
}

顾名思义,就是反向的,it++时是从后往前走的。 


 const修饰的反向迭代器

string::const_reverse_iterator it = s1.rbegin();
while (it != s1.rend())
{
	cout << *it << " ";
	it++;
}

方法四:范围for(底层原理是迭代器)

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

auto 是实现自动识别类型,    如  char ch = ‘a’;      auto i = ch;   则i的类型是char

 这个代码的意思是   依次把s1中的字符串变量中的字符拷贝给u变量,然后打印u变量

如果想要对里面的值进行修改,就需要用引用传参。 

for(auto& u:s1)
{
    u++;
    cout<<u<<" ";
}

其他:还有两个成员函数back和front,分别访问最后一个字符和第一个字符。 

cout<<s1.back();

cout<<s1.front();


 三、string类的容量接口:

3.1 size  和  length
s1="hello world";
cout<<s1.size()<<endl;
cout<<s1.length()<<endl;

两者没有区别,一开始是length先出来的,然后为了和后面的STL中的其他容器保持一致,就多了一个size函数。我们一般用size

3.2 capacity

capacity返回的容量不包括\0,就是本来开了16的空间,但是前人认为\0不能算有效容量,就会返回15

3.3 empty
string s1;
string s2="hello world";
cout<<s1<<endl;
cout<<s2<<endl;

 

3.4 clear

将size变为0,相当于清理数据,下一次是从size==0的位置开始插入数据。 

string s2="hello world";
s2.clear();
s2 = "C++";
cout << s2;

 

3.5  reserve 预留空间   和    resize  调整size的大小,其间用\0来填充
string s1="hello world";
string s2="hello world";
s2.reserve(100);
cout<<s2.size()<<" "<<s2.capacity() << endl;

s1.resize(100);
cout << s1.size() <<" "<< s1.capacity() << endl;

这样子的结果是如果是resize出来的,那么前面的11--100的空间都被\0填充,我们只能从100号位置开始填充,中间的空间都浪费掉了。

而reserve出来的没有改变size的大小,就扩容。(这个函数就很nice,当你提前知道你想要开多大的空间,就可以使用这个函数。

看resize函数的代码

string s1="hello world";
s1.resize(15);
s1 += "hi";
cout << endl;
cout << s1 << endl;
cout << s1.size() <<" "<< s1.capacity() << endl;

\0是不打印出来的,所以最后的结果是

world和hi中间是没有空格的

四、String类对象修改接口

 4.1 这里就operator+=这个函数重要

可以实现尾插字符,也可以实现尾插字符串

第四个是C++11之后添加的(我还不知道是什么,等我学了再谈) 现在我们就用前三种。

string s1 = "hello world";
s1 += ' ';
s1 += "hello C++";
cout << s1;

4.2  insert和earse

都是需要挪动数据,所以时间复杂度高,少用。

string s1 = "hello world";
s1.insert(5,"         ");
cout << s1;

 从下标为5位置开始插入

string s1 = "hello world";
s1.insert(5,"         ");
s1.erase(0,5);
cout << s1;

 从下标为0的位置,开始删除5个元素

string类里面的erase函数是有缺省值的。

       当不传参的时候是从0号位置开始删除到末尾。

         传一个参数是指从n号位置开始一直删除到末尾。

4.3 replace

函数的参数分别是替换的起始位置,删除的数据大小,替换的字符串(不能是字符)

 

把含有a b c的字符都替换成 字符 *

其他   reverse(这个不是string类里面的,而是迭代器中的)

逆置字符串,参数是迭代器s1.begin()和s1.end()

s1="hello world";
reverse(s1.begin(),s1.end());
cout<<s1;

就可以把s1中的字符串逆转,当然,也适用于其他的迭代器。 

五、 String对象字符串运算相关接口

5.1 c_str 

 两者的类型不同。

一个是string对象

一个是字符指针(也就是字符串)

5.2 find

第一种:

        string s1 = "hello world";
        string s2 = "world";
        size_t posn=s1.find(s2, 1);   
        cout << posn;

// 从s1这个对象中查找s2这个对象,从下标为1这个位置查找(默认从0开始查找)

返回  匹配的第一个字符的下标

第二种:

        string s1 = "hello world";
        string s2 = "world";
        size_t posn = s1.find("hello");     //第一个参数是输入字符串       第二个参数默认为0
        cout << posn;

第三种:

 参数1、查找的字符串   参数2、从第几个位置开始查找   参数3、要匹配的字符序列的长度。

 string str("There are two needles in this haystack with needles.");
 found = str.find("needles are small", 15, 6);
 if (found != std::string::npos)
 cout << "second 'needle' found at: " << found << '\n';

 

从下标为15的位置开始查找,找到needle这个字符串(查找这个字符串的前6个字符)

第四种:

查找字符

    string str("There are two needles in this haystack with needles.");
    found = str.find('.');
    if (found != std::string::npos)
    cout << "Period found at: " << found << '\n';

5.3 rfind   

        就是倒着找,从后往前找 

        同样是四个重载 

5.4 substr

 从第pos位置开始截取len长度的字符串。参数2没有时,取到结尾。

#include <iostream>       // std::cout
#include <string>         // std::string

int main()
{
    std::string str = "We think in generalities, but we live in details.";
    // (quoting Alfred N. Whitehead)

    std::string str2 = str.substr(3, 5);     // "think"

    std::size_t pos = str.find("live");      // position of "live" in str

    std::string str3 = str.substr(pos);     // get from "live" to the end

    std::cout << str2 << std::endl << str3 << '\n';

    return 0;
}

5.5 getline(string)

两种重载,第一种需要自己手动设置结束符    第二种是以\n为结束符

//第一种
int main()
{
	std::string name;
	std::cout << "Please, enter your full name: ";
	std::getline(std::cin, name,'a');
	std::cout << name;
	return 0;
}

 以‘a’字符为结束标志


//第二种
#include <iostream>
#include <string>
int main()
{
	std::string name;
	std::cout << "Please, enter your full name: ";
	std::getline(std::cin, name);
	std::cout << name;
	return 0;
}


好了,到这里本篇文章就结束了,如果有什么错误的地方,还请各位大佬指明,相互学习。

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

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

相关文章

负债56亿,购买理财产品遭违约,操纵虚假粉丝,流量在下滑,客户数量减少,汽车之家面临大量风险(九)

本文由猛兽财经历时5个多月完成。猛兽财经将通过以下二十二个章节、8万字以上的内容来全面、深度的分析汽车之家这家公司。 由于篇幅限制&#xff0c;全文分为&#xff08;一&#xff09;到&#xff08;十&#xff09;篇发布。 本文为全文的第二十二章。 目录 一、汽车之家公…

Transformers:它们如何转换您的数据?

一、说明 在快速发展的人工智能和机器学习领域&#xff0c;一项创新因其对我们处理、理解和生成数据的方式产生深远影响而脱颖而出&#xff1a;Transformers。Transformer 彻底改变了自然语言处理 &#xff08;NLP&#xff09; 及其他领域&#xff0c;为当今一些最先进的 AI 应…

代码随想录-二叉树(节点)

目录 104. 二叉树的最大深度 题目描述&#xff1a; 输入输出描述&#xff1a; 思路和想法&#xff1a; 111. 二叉树的最小深度 题目描述&#xff1a; 输入输出描述&#xff1a; 思路和想法&#xff1a; 222. 完全二叉树的节点个数 题目描述&#xff1a; ​输入输出描…

关于举办《Llama3关键技术深度解析与构建Responsible AI、算法及开发落地实战》线上高级研修讲座的通知

关于举办《Llama3关键技术深度解析与构建Responsible AI、算法及开发落地实战》线上高级研修讲座的通知

Mybatis逆向工程的2种方法,一键高效快速生成Pojo、Mapper、XML,摆脱大量重复开发

一、写在开头 最近一直在更新《Java成长计划》这个专栏&#xff0c;主要是Java全流程学习的一个记录&#xff0c;目前已经更新到Java并发多线程部分&#xff0c;后续会继续更新&#xff1b;而今天准备开设一个全新的专栏 《EfficientFarm》。 EfficientFarm&#xff1a;高效农…

Tomcat PUT方法任意写文件漏洞(CVE-2017-12615)

1 漏洞原理 在Apache Tomcat服务器中&#xff0c;PUT方法通常用于上传文件。攻击者可以通过发送PUT请求&#xff0c;将恶意文件上传到服务器。 当攻击者发送PUT请求时&#xff0c;Tomcat服务器会将请求中的数据写入指定的文件。如果攻击者能够控制文件路径&#xff0c;那么他们…

Mac brew安装Redis之后更新配置文件的方法

安装命令 brew install redis 查看安装位置命令 brew list redis #查看redis安装的位置 % brew list redis /usr/local/Cellar/redis/6.2.5/.bottle/etc/ (2 files) /usr/local/Cellar/redis/6.2.5/bin/redis-benchmark /usr/local/Cellar/redis/6.2.5/bin/redis-check-ao…

跨平台桌面客户端开发框架

跨平台桌面客户端开发框架允许开发者创建能够在多个操作系统上运行的桌面应用程序。以下是一些流行的跨平台桌面客户端开发框架。这些框架各有优势&#xff0c;选择哪个框架取决于项目需求、团队的技术栈以及对特定特性的偏好。 1.Electron &#xff1a; 使用JavaScript, HTML…

Java IO流(三)

1. 字符流 1.1 什么是字符流 在Java中&#xff0c;字符流是指提供了基于字符的I/O能力的API。 Java 1.0中提供的基于字节的I/O流API只能支持8位字节流&#xff0c;无法妥善地处理16位Unicode字符。由于需要支持Unicode处理国际化字符&#xff0c;因此Java 1.1 对基础流式I/O库…

vue3+ts 原生 js drag drop 实现

vue3ts 原生 js drag drop 实现 一直以来没有涉及的一个领域就是 drag drop 拖动操作&#xff0c;研究了下&#xff0c;实现了&#xff0c;所以写个教程。 官方说明页面及实例&#xff1a;https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API 最终效果&…

SpringCloud整合Gateway结合Nacos

目录 一、引入依赖 二、开启两个测试项目 2.1 order service ​编辑 2.2 user service 三、gateway项目 3.1 新建一个bootstrap.yml文件 3.2 将我们的的网关配置写道nacos里的配置里 3.3 测试&#xff1a;看能够根据网关路由到两个测试的项目 四、 优化 4.1 将项目打包…

数据库(MySQL)—— DQL语句(聚合,分组,排序,分页)

数据库&#xff08;MySQL&#xff09;—— DQL语句&#xff08;聚合&#xff0c;分组&#xff0c;排序&#xff0c;分页&#xff09; 聚合函数常见的聚合函数语法 分组查询语法 排序查询语法 分页查询语法 DQL的执行顺序 我们今天来继续学习MySQL的DQL语句的聚合和分组查询&…

【面试经典 150 | 数组】接雨水

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;预处理方法二&#xff1a;单调栈方法三&#xff1a;双指针 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff…

【大模型学习】Transformer(学习笔记)

Transformer介绍 word2vec Word2Vec是一种用于将词语映射到连续向量空间的技术&#xff0c;它是由Google的Tomas Mikolov等人开发的。Word2Vec模型通过学习大量文本数据中的词语上下文信息&#xff0c;将每个词语表示为高维空间中的向量。在这个向量空间中&#xff0c;具有相似…

【C++】学习笔记——string_2

文章目录 六、string类2. 反向迭代器const迭代器 string类对象的容量操作&#xff08;补&#xff09;size() 3. string类的元素访问4. string类的修改 未完待续 结合文档食用~ 六、string类 2. 反向迭代器 一般来说&#xff0c;迭代器都是正向的遍历容器&#xff0c;虽然可以…

LuaJIT源码分析(三)字符串

LuaJIT源码分析&#xff08;三&#xff09;字符串 要表示一个字符串&#xff0c;核心就是需要知道字符串的长度&#xff0c;以及存放字符串具体数据的地址。lua的字符串是内化不可变的&#xff0c;也就是lua字符串变量存放的不是字符串的拷贝&#xff0c;而是字符串的引用。那么…

C语言⼆级指针如何操作字符串数组(指针数组)?

一、问题 对于字符串数组该如何操作&#xff08;⽽且是使⽤指针数组存储&#xff09;&#xff1f; 二、解答 使⽤指针的指针实现对字符串数组中字符串的输出。指向指针的指针即是指向指针数据的指针变量。这⾥创建⼀个指针数组 strings&#xff0c;它的每个数组元素相当于⼀个…

springcloud自定义全局异常

自行创建一个实体类 /*** 全局异常处理类**/ ControllerAdvice public class GlobalExceptionHandler {ExceptionHandler(Exception.class) ResponseBody public Result error(Exception e){e.printStackTrace(); return Result.fail();}/*** 自定义异常处理方法* param e * re…

神奇的Vue3 - 组件探索

神奇的Vue3 第一章 神奇的Vue3—基础篇 第二章 神奇的Vue3—Pinia 文章目录 神奇的Vue3了解组件一、注册组件1. 全局注册​2. 局部注册3. 组件命名 二、属性详解1. Props&#xff08;1&#xff09;基础使用方法&#xff08;2&#xff09;数据流向&#xff1a;单项绑定原则&…

深入图像分类:使用美国手语数据集训练定制化神经网络

引言 在前一篇博客中&#xff0c;我们探讨了如何使用MNIST数据集训练一个基础的神经网络来进行手写数字识别。在本文中&#xff0c;我们将更进一步&#xff0c;使用美国手语字母表&#xff08;ASL&#xff09;数据集来构建一个定制化的图像分类模型。通过这个过程&#xff0c;…