string的介绍

news2024/10/5 17:26:48
string是c++中表示字符串的字符串类,要使用需要包头文件:#include<string>
先了解一下string的一些信息
string看起来是一个类,但实际上是typedef的模板。
在cplusplus.com网站上,string的相关信息
模板的实例化结果有以下几个,其中string最常用,有些情况下也会用到u16string,u32string,wstring
由图可以看出,这些实例化的结果都是由basic_string模板实例化得来的,至于为什么要用不用的类型来实例化出这些类,就涉及到编码了。
世界上第一个编码是ASCII码,Unicode(utf-8,utf-16,utf-32),gbk也是编码
有了ASCII码表,就能通过计算机储存的二进制数字表示出信息。
但是ASCII码表仅能表示英文,随着经济全球化,计算机上需要显示其他国家的语言。
比如汉语,汉语的复杂,一个字符无法表示清楚(一个字符的大小时1字节,仅能表示256个不同信息),所以就用2个字符表示。不常见的汉字可能会用到3~4个字节。gbk就是针对汉语的编码表。而Unicode则是针对全世界的文字设计的编码表,其内容更复杂。Unicode和gbk都是要兼容ASCII码的。
Windows下的编码一般使用gbk,Linux下的编码一般使用utf-8。
string为什么要设计成模板?
因为string是char实例化出来的,能很好的对应英文,但是gbk中的字符,也就是汉字,需要两个字节,一个字符不在对应一个char,所以设计出新的的类型char16_t(对应utf16),wchar_t(对应gbk),模板是为了应用到不同的场景(即存在不同的编码)中,比如在char16_t中一个字符就不是一个字节了。所以对不同类型(char16_t等)的字符串进行处理时,需要用到不同的类(u16string等)。
string类具有一些优秀的功能,比如string类中字符串的连接,直接+=就可以。
int main()
{
    string sql = "hello ";
    sql += "world" 
    cout << sql << endl;
    return 0;
}
会打印出:hello world
cplusplus.com
cppreference.com
c++的学习要学会看文档,上面两个是看文档的网站
第一个内容更合理,看起来比较舒服。
第二个是c++的官方网站,语法都是最新的。但是整体内容相对第一个比较乱。
官方网站叫这个的原因是第一个网站名被抢注了。
这两个网站都是英文文档,没有好的中文文档,从这里开始要学会看英文文档了,这是必备的技能之一。
英语不好的同学可以建立一个常见英文库,毕竟这是某一领域的英文文献,词汇量并不大。
下面使用的图片都是第一个网站的。
string这样的知识点通常分为几个板块
最前面的: 头文件包含。
第一个,类的声明:介绍string
第二个,对string的一些说明
重点关注的是下面几个板块:
成员函数:成员函数的种类为4个。第一个包含了构造函数和拷贝构造函数。
迭代器:所有数据结构容器都有
容量相关的接口:
访问相关的接口:
还有修改相关函数接口(modifiers),操作相关函数接口( String operations
还有非成员函数的重载:
毕竟不可能记住全部的函数,所以查文档是程序员必备的技能。
string的使用
string是管理动态增长的字符数组,这个字符串以\0结尾。
#include<string>
int main()
{
    //string s;这样会报错,编译器不认识string,string未声明
    std::string s; //因为string属于std,c++标准库的内容都是放到std的
    return 0;
}
首先看看构造函数的情况,单击框起来的部分
并不是每个函数都要学,string相关函数就有100+个,每个都学根本记不住,学常用的就可以了。遇到不常用的情况才查文档。
常用的如上图
int main()
{
    string s1; //构造一个无参的string,但是会有一个\0
    string s2("hello world"); //构造一个常量字符串初始化的string
    s2 += "!!!"; //动态增长意为着可以增删查改,string的空间是从堆上申请的,增的方式就是+=
    string s3(s2);
    string s4 = s3; //s3,s4则是拷贝构造函数
    //下面介绍一些不常用的
    string s5(" https://cplusplus.com", 5); //第5个函数,意为使用字符串的前5个字符来进行初始化
    string s6(10, 'a'); //第6个函数,意为使用10个字符'a'来初始化
    string s7(s2, 6, 3); //第3个函数,意为使用s2的一部分来初始化,6代表从下标为6的位置开始,3代表从开始位置的后三个字符,len有一个npos的缺省值,npos是属于string类里面的一个静态成员变量,大小为整形最大值,len如果超过字符串的长度了,就有多少取多少。
    return 0;
}
每个板块的下面都是有相应的函数说明,所以不用担心函数中出现的变量不知道什么意思。
析构函数是自动调用的,没什么需要特别说明的
赋值重载函数:
int main()
{
    string s1("hello");
    string s2("!!!");
    s2 = s1; //赋值后,s2会变成hello,而不是!!!lo
    s1 = "wor"; // 赋值后,s1会变成wor,而不是worlo
    s1 = 'a'; // 赋值后,s1会变成a,而不是aorlo
    return 0;
}
后两个函数用的不多,string设计的比较早,所以很多设计不成熟,出现了冗余。
简单展示一下string的底层原理
template T
class basic_string
{
public:
    basic_string(const T* str) //这只是介于string的简单实现,实际情况要考虑各种类型,比这个复杂
    {
        size_t len = strlen(str);
        _str = new T[len + 1];
        strcpy(_str, str);
        //不能不开辟空间,直接赋值,即:_str = str;
        //如果str是一个常量字符串的指针,_str就指向了一个常量字符串,这样就不能扩容了
    }
private:
    T* _str;
    size_t size;
    size_t capacity;
};
对于传统的数组,检查越界是一种抽查;而对于string,只要越界就会报错。
如何遍历string中的每个字符?
在C语言中,我们使用指针来遍历字符串,在c++中还使用指针吗?
int main()
{
    string s1("hello world");
    //方式一:下标+[]
    //c++有[]运算符重载
    for(size_t i = 0; i < s1.size(); i++) //s1.size()是一个函数,计算字符串长度,不包含\0
    {
        cout << s1[i] << " "; //相当于调用函数s1.operator[](i);
    }
    //方式二:迭代器
    string::iterator it = s1.begin(); //it是迭代器名称,和变量名类似,s1.begin()函数返回开始位置的迭代器
     //iterator是内嵌在string中的,iterator是string的内嵌类型,iterator是在string中定义或者typedef出来的
    //通过这样的方式来取迭代器的类型,如果是vector的迭代器:vector<int>::iterator
    while(it != s1.end())
    //s1.end()函数返回结束位置(最后一个字符)的下一个位置(\0)的迭代器,是一个左闭右开的关系,为了方便遍历,所有迭代器都符合这个关系, it != s1.end() 是迭代器的标准用法,有人会想用 it < s1.end(),这个只能在地址空间连续的情况使用
    {
        cout<< *it << " ";
        it++;
    }
    //迭代器是像指针一样的东西,或者就是指针,具体看底层实现
    //所有的迭代器都是差不多的,基本上学会string的迭代器用法,就会其他迭代器的用法了。
    //自己写的数据结构也可以有自己写的迭代器,符合某一行为的都可以称为迭代器
    //迭代器前期最好自己写,后期熟悉了可以用auto自动推导。
    //方式三:范围for,c++11
    for(auto a : s1) //范围for其实就是被替换的迭代器
    {
        cout << a <<" "; //自动取字符,自动++
    }
    return 0;
}
由图中可知,operator[]是引用返回,引用返回处了可以减少拷贝,还有一个作用:支持修改返回对象,比如在交换string中的两个字符时,s[a]和s[b]直接就交换了,不用取地址,再解引用。
在早期,还有一个at函数接口,其功能和[]一样。取下标位置为0的字符:s[0] / s.at(0)
但是还是存在区别,比如访问越界时,[]直接断言报错,s.at()则是抛异常
迭代器分四种
第一个就是之前写的正向迭代器
string s1("hello world");
string::iterator it = s1.begin();
while(it != s1.end())
{
    cout<< *it << " ";
    it++;
}
第二种迭代器:反向迭代器
string s1("hello world");
string::reverse_iterator rit = s1.rbegin(); //rbegin()在字符串\0的前一个位置
while(rit != s1.rend()) //rend()在字符串首元素的前一个位置
{
    cout << *rit << " ";
    rit++; //反向迭代器的++是按反的方向走
}
单链表就没有反向迭代器
迭代器的功能和下标类似,现在的string和后面的vector都可以选择下标,到list就只能选择迭代器了。
迭代器类似指针,所以迭代器除了读取数据,还可以修改数据,如果string引用传参给const string类型,就意味着不能用string类型的对象来写迭代器了,必须用const string类型的对象来写迭代器。
void func(const string& rs) //只能读的正向迭代器
{
    string::const_iterator it = rs.begin();
    while(it != rs.end())
    {
        cout<< *it << " ";
        it++;
    }
    string::const_reverse_iterator rit = rs.rbegin(); //只能读的反向迭代器
    while(rit != rs.rend())
    {
        cout << *rit << " ";
        rit++;
    }
}
int main()
{
    string s1("hello world");
    func(s1);
    return 0;
}
这就是剩下的第三,第四种迭代器。
c++11为了更规范化,添加了cbegin等函数,用来解决被const修饰的string,但是begin()函数的自动识别就挺好用的。
size的功能是返回有效字符的个数,length也一样。因为string诞生的时间比STL早,所以当时使用length表示字符个数更合适,STL出现后,因为树等数据结构不适合用length表示,所以用size表示元素个数。string也是管理字符串的数据结构,就有了size函数,实际应用中size使用的更多一点。
上面代码是观察扩容规律的。
除了第一次,其余都是1.5倍扩。
扩容是存在损耗的,有没有办法减少扩容?
这个函数可以减少扩容。注意:reserve不是reverse
reserve可以根据需要提前开好足够的空间(容量会被修改,不是多扩1000)
string s;
s.reserve(1000);
size_t sz = s.capacity();
cout << "making s grow:\n";
……
涉及对齐问题,所以实际中会多开一点。
还有一个resize函数
s.resize(1000);
和reserve相比,resize除了会扩空间外,还会将空间内容初始化为\0。如果不想是\0,还可以自己定义初始化内容
s.resize(1000, 'x');
观察不同函数带来的效果
string s("hello");
s.reserve(100);
s.resize(100); //s.reserve(100);已被注释掉
已经存在5个元素,补95个'x'。
在s.resize(100);的情况下,
再添加两行代码:
s.reserve(10); //结果无变化,说明reserve不会缩
s.resize(10); //容量无变化,元素个数改变了
string增删查改
插入一个字符可以用push_back,插入字符串可以用append,append很像构造函数。图示两个最常用。
但尾插最好用的是+=。
头插或者中间插要用到insert,删除则要用到erase。
图示的两个为最常用的两个。
第二个函数只会删除一个字符
swap函数
string s1("hello");
string s2("world");
s1.swap(s2);
swap(s1,s2);
这两个swap有什么区别吗?
第一个swap效率高,第二个swap效率低,这是c++98,c++11情况又不一样了。
因为s1,s2分别指向一个空间,所以第一个swap的交换,改变的是指向空间的方向,第二个swap的交换是深拷贝交换。
c_str函数
c_str就是返回c形式的字符串,即返回指向字符串的指针。一般的string对象包含三个成员:指针,size,capacity,c_str就是把指针单独拿出来使用。
主要的功能是和c的一些接口(函数)形成配合。
cout << s1 <<endl;
cout << s1.c_str() << endl;
两者都是打印s1中存储的字符串,但第一个是string的流插入重载,第二个则是字符串打印。
有时我们需要取出文件的后缀名
string file("test.cpp");
这时就需要find函数,确定要找到的位置
string file("test.cpp");
size_t pos = file.find('.');
if(pos != string::npos)
{
    string suffix = file.substr(pos, file.size() - pos);
    cout << file.c_str << "后缀:" << suffix.c_str <<endl;
}
else
{
    cout << "没有后缀" << endl;
}
substr是取字符串子串的函数
取出pos位置后len个位置的子串。
但是存在包含多个字符 '.' 的文件,比如:test.c.tar.zip
这时就需要反着找第一个字符 '.'
string file("test.c.tar.zip");
size_t pos = file.rfind('.');
if(pos != string::npos)
{
    string suffix = file.substr(pos);
    cout << file.c_str << "后缀:" << suffix.c_str <<endl;
}
else
{
    cout << "没有后缀" << endl;
}
统一资源定位符(Uniform Resource Locator,URL)是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及 浏览器应该怎么处理它。
考虑的再复杂一点,分离网址中的域名
string url1(" https://www32.cplusplus.com/reference/string/string/rfind/");
string url2(" 力扣");
://之前的部分称为协议,://到第一个/之间的部分称为域名,剩下的部分是类似目录一样的东西,
string url1(" https://www32.cplusplus.com/reference/string/string/rfind/");
string url2(" 力扣");
string& url = url1; //这里用引用url1,再需要分离其他网址的域名,就不需要修改下面的url,在这里改变引用对象就行
string protocol;
size_t pos = url.find("://"); //返回的是字符 ':' 位置的下标
if(pos != string::npos)
{
    protocol = url.substr(0, pos);
    cout << "protocol: " << protocol << endl;
}
else
{
    cout << "网址非法" << endl;
}
string domain;
size_t pos1 = url.find('/', pos + 3); //下面有说明
if(pos1 != string::npos)
{
    domain = url.substr(pos + 3, pos1 - (pos + 3));
    cout << "domain: " << domain << endl;
}
find函数还有一个pos参数,可以决定从哪开始找
getline函数
适用于手动输入包含字符 ' ' 的字符串,getline函数只认换行符,不像cin,遇到字符 ' ' 就停止了。
getline的第一个函数是输入流,即cin。
C语言中有让字符串转变为整型的函数:atoi和让整数变成字符串的函数:itoa
value代表要转换成字符串的整数, str是内存中的数组,用于存储结果以 null 结尾的字符串,base是基于什么进制对value进行转换,范围:2~36,返回值是 指向生成的以 null 结尾的字符串的指针,与参数str相同。
c++提供和了string配合更便捷的函数
在头文件<string>中可以看到
//size_t* idx可以暂时不用管
string在一些地方还有一种设计叫:引用技术的浅拷贝或者叫显式拷贝,不过快被淘汰了,了解一下就行
大致思路:
string s1("hello world");
string s2(s1);
需要深拷贝s1,代价太大,就进行浅拷贝。
进行浅拷贝需要解决析构问题,解决方案是引入一个引用计数的概念,s1,s2指向相同空间,s1引用计数为1,s2引用计数为2。有几个对象指向这块资源,引用计数就为几。s2结束时,引用计数为2,引用计数自减1,当结束时引用计数为1,说明是最后一个使用该资源的对象,才会释放空间。
还要解决一个对象修改,会影响其他对象的问题。解决方案是看引用计数,不为1则说明还要其他对象要用,这时进行深拷贝。这叫写时拷贝,也称为延时拷贝。
也就是说,只有不修改对象,这个设计的作用才能最大化。

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

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

相关文章

【unity小创意】相机的正反操作实现场景的二维跳跃

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

Spring的第十二阶段(01):Spring实现AOP的简单使用

1、使用Spring实现AOP简单切面编程 需要导入工程的jar包 Spring的核心包 spring-beans-4.0.0.RELEASE.jar spring-context-4.0.0.RELEASE.jar spring-core-4.0.0.RELEASE.jar spring-expression-4.0.0.RELEASE.jarSpring的测试包 spring-test-4.0.0.RELEASE.jarSpring日记相…

【网络安全】这套面试题,让你提前预判面试官的预判!

最近这个帖子的点赞和收藏变高起来了&#xff0c;许多小伙伴在问我安全大厂的面试题没有&#xff0c;我准备利用一些时间把这套面试宝典整理一下&#xff01; 今天有同学拿着他准备的面试问题清单给我看&#xff0c;看还有没有遗漏的&#xff0c;我看了下&#xff0c;觉得还是…

Spring-Bean管理-注解

组件注册 Component/Controller/Service/Repostory :注册自定义组件到容器中 加上约定的注解。 在Configuration注解的类中配置包扫描器 ComponentScan(vlaue "cn.shaoxiongdu") Configuration: 标注配置类 Scope &#xff1a; 配置是否为单实例 prototype: 多实…

JVM与GC

Java:跨平台的语言 write once, run anywhere JVM&#xff1a;跨语言的平台 Java虚拟机根本不关心运行在其内部的程序到底是使用何种编程语言编写的&#xff0c;它只关心“字节码”文件。 Java不是最强大的语言&#xff0c;但是JVM是最强大的虚拟机。 JVM的整体结构 这个架构…

Java基础学习(12)

Java基础学习 一、不可变集合二、Stream流2.1 Stream流数据添加2.2 Stream流的中间方法2.3 Stream终结方法 三、 方法引用3.1 方法引用的基本概念3.2 方法引用的分类3.2.1 引用静态方法 3.2.2 引用成员方法3.2.3 引用构造方法3.2.4 使用类名引用成员方法3.2.5 引用数组的构造方…

PyQt5 基础篇(一)-- 安装与环境配置

1 PyQt5 图形界面开发工具 Qt 库是跨平台的 C 库的集合&#xff0c;是最强大的 GUI 库之一&#xff0c;可以实现高级 API 来访问桌面和移动系统的各种服务。PyQt5 是一套 Python 绑定 Digia QT5 应用的框架。PyQt5 实现了一个 Python模块集&#xff0c;有 620 个类&#xff0c;…

MATLAB 点云非均匀体素下采样 (8)

MATLAB 点云非均匀体素下采样的不同参数效果测试 (8) 一、实现效果二、算法介绍三、函数说明3.1 函数3.2 参数四、实现代码(详细注释!)五、与固定步长采样法比较5.1 代码5.2 效果一、实现效果 不同参数调整下的非均匀体素下采样结果如下图所示,后续代码复制黏贴即可: 可…

入职6个月,被裁了...

我跟大多数人不大一样&#xff0c;从来没有说要等公司主动裁员拿补偿&#xff0c;我看自己没有什么价值或者是公司不行了&#xff0c;我都会主动离职。但是这次也太突然了。公司很大已上市&#xff0c;并不是不行了&#xff0c;总结原因就是&#xff0c;一是领导无能&#xff0…

【STM32CubeMX】F103ADC获取

前言 本文记录了我学习STM32CubeMX的过程&#xff0c;方便以后回忆。我们使用的开发板是基于STM32F103C6T6的。本章记录了基本的ADC值的获取流程&#xff0c;只单纯地记录了ADC端口的配置&#xff0c;没有加配像串口之类的调试&#xff0c;以简化流程。下面的流程是使用串口调试…

版本控制系统Git - 配置与基本使用

Git 1 Git简介1 Git概述2 Git的作用2.1 项目版本管理2.2 多人协同开发2.3 Git 的结构2.4 Git的工作原理 2 Git安装1 下载Git2 安装Git3 配置环境变量4 测试git是否安装成功5 安装git桌面工具(可以不安装) 3 Git基本操作1 设置Git用户2 新建仓库3 查看仓库状态4 添加到暂存文件5…

MATLAB 点云重复点去除(7)

MATLAB 点云重复点去除 (7) 一、实现效果二、算法介绍三、函数说明3.1 函数3.2 参数四、具体代码 (注释详细!)一、实现效果 效果上看不出来,但实际上左边的点云是右边的两倍 二、算法介绍 重复点的去除,是点云处理中常用的预处理方法,因为重复点的存在有时候会严重干…

C++系列四:数组

数组 1. 数组定义与初始化2. 多维数组3. 字符数组4. 总结 1. 数组定义与初始化 定义数组时需要指定数组的类型和大小&#xff1a; int myArray[10];上述代码定义了一个包含 10 个整数的数组。这些整数的下标从 0 开始&#xff0c;并以 1 个单位递增。 C 允许在定义数组时对其…

设计模式——装饰者模式(继承和接口的两种实现方式)

是什么&#xff1f; 场景案例&#xff1a;想必大家都吃过手抓饼吧&#xff0c;我们在点手抓饼的时候可以选择加培根、鸡蛋、火腿、肉松等等这些配菜&#xff0c;当然这些配菜的价格都不一样&#xff0c;那么计算总价就比较麻烦&#xff1b; 装饰者模式就是指在不改变现有对象…

springboot 集成 shardingSphere 加mybatisplus 自带增加 分页查询 和源代码包 分库分表 单库 分表 使用雪花算法id

目录 介绍 代码下载 效果 数据库 代码结构 上代码 pom.xml yml配置 建表语句 mapper.xml mybatisplus 配置.java logback application.java BaseEntity TUser TUserMapper TUserService TUserServiceImpl TUserController 测试 介绍 这套springboot shardi…

OSG笔记:AutoTransform实现固定像素大小的图形

需求 在(200,0,0)位置绘制固定10像素大小的正方体 实现方式 为了便于观察&#xff0c;例子中绘制了两条直线&#xff0c;相交于(200,0,0)。 //两根直线交于(200, 0, 0)&#xff0c;用于辅助观察 {osg::Geometry* pLineGeom new osg::Geometry();osg::Vec3Array* pVertexArra…

python语法复习

print&#xff1a;输出函数 print(520)效果&#xff1a;输出520. print(hello)效果&#xff1a;输出hello. print(1020)【效果&#xff1a;输出了:1020】注&#xff1a;“ ”在print里面是一个连接符。 print(1020)【效果&#xff1a;输出了30】注&#xff1a; 在此处…

OJ 系统常用功能介绍 快速入门 C++ Python JAVA语言在线评测

技术支持微 makytony 服务器配置 腾讯云 2H4G 5M 60GB 轻量应用服务器 承载大约 200~400人使用&#xff0c;经过压力测试&#xff0c;评测并发速度可满足130人左右的在线比赛。 系统镜像选 Ubuntu 22.04 LTS &#xff0c;Ubuntu是最热门的Linux发行版之一&#xff0c;是一款…

洞车系统常见问题解决指南

洞车常见问题解决指南 1.研发脚本处理问题1.1 WMS出库单无法审核1.2 OMS入库单无法提交&#xff0c;提示更新中心库存失败1.3 当出现OMS下发成功WMS/TMS/DMS还没有任务的情况时处理方案1.4 调度波次生成或者添加任务系统异常1.5 东鹏出库单部分出库回传之后要求重传1.6 更新订单…

spring、springmvc、springboot、mybatis框架高频面试题

一、spring 1、Spring框架中的单例bean是线程安全的吗&#xff1f; 不是线程安全的&#xff0c;是这样的&#xff1a; 当多用户同时请求一个服务时&#xff0c;容器会给每一个请求分配一个线程&#xff0c;这是多个线程会并发执行该请求对应的业务逻辑&#xff08;成员方法&a…