Cpp::STL—string类的使用与理解(上)(8)

文章目录

  • 前言
  • 一、string类对象的构造函数
    • string()
    • string(const char* s)
    • string(size_t n, char c)
    • string(const string& s)
    • string(const string& str,size_t pos,size_t len = npos)
  • 二、string类对象的容量操作
    • size与length
    • capacity
      • capacity返回值比size大
      • capacity的扩容机制
    • empty
    • clear
    • shrink_to_fit
    • reserve
      • 关于reserve与扩容的一些问题
    • resize
      • 字符串变短(n>size)
      • 字符串在容量内变长(capacity>=n>size)
      • 字符串修改长度超出容量(n>capcity)
  • 三、string类对象的访问
    • operator[ ]
    • 迭代器(简单介绍)
      • 反向迭代器
  • 四、string类对象的遍历操作
    • for+[ ]
    • 迭代器(begin(),end())
    • 范围for
  • 总结


前言

  C语言中,字符串是以’\0’结尾的一些字符的集合(C-string),为了操作方便,C标准库中提供了一些 str 系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
  这就是我们为什么学习string类的理由,如果你对前面类和对象的内容了解透彻的话,学习STL按道理来说应该不算太难

另外,我还想说,string类其实是当时发布STL时候的前排兵,说白了就是没什么可供参考,所以你在学习它的时候有时候会感觉很挫很冗余,这就对了,这就是正确的感觉


一、string类对象的构造函数

在这里插入图片描述

  事先声明,就像我前言说的一样,string设置的很冗余,所以我挑选几个常见的来讲,甚至于有几个我挑出来的也不多见,下文同理

(constructor)函数名称功能说明
string() (重点)默认构造,创建一个空串,这个空串的长度是0
string(const char* s) (重点)用C-string来构造string类对象
string(size_t n, char c)通过一个字符来构造,一个字符重复n次
string(const string& s) (重点)拷贝构造函数
string(const string& str,size_t pos,size_t len = npos)(重点)从str中pos指向位置先后拷贝len长度字符,两种情况下文给出

string()

功能:构造空string类对象,其长度为0

#include<iostream>
#include<string>
using namespace std;int main()
{string s1;cout<<s1.length()<<endl; // 0return 0;
}

string(const char* s)

功能:使用C-string构造string类对象。在非空字符串中,从s指向位置拷贝一份字符串

#include<iostream>
#include<string>
using namespace std;int main()
{// 相当简便string s1("Hello,world!");cout << s1 << endl; // Hello,world!// 其实我们还有一种清晰明了的方法const char* s = "Hello,world!";string s2(s);cout << s2 << endl; // Hello,world!return 0;
}

string(size_t n, char c)

功能:通过一个字符来构造,一个字符重复n次

#include<iostream>
#include<string>
using namespace std;int main()
{string s1(5,'r');cout << s1 << endl; // rrrrrreturn 0;
}

string(const string& s)

功能:拷贝构造,通过已有对象拷贝构造一个新的对象,这个对象和已有对象在逻辑上是相同的

#include<iostream>
#include<string>
using namespace std;int main()
{// 这两种都称为拷贝构造string s1("Hello,world!");string s2(s1);string s3 = s1;return 0;
}

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

功能:从str中pos指向位置先后拷贝len长度字符。出现两种结果:拷贝到str最后一个字符或没有达到最后一个字符完成拷贝

  第三个参数len类型为 size_t ,而缺省值 npos == -1 导致了 npos 按补码形式是32个比特位1,而又被当作正数还原为原码,就是 INT_MAX(涉及到编码那块,考验你前面学得扎不扎实的时候到了)。所以对于当没有明确 len 数值,默认是从 pos 位置拷贝字符串到最后一个字符,而如果str已经拷贝到最后一个字符了,那就结束拷贝

这是原文翻译,你英语好的话你自己翻译~
Copies the portion of str that begins at the character position pos and spans len characters (or until the end of str, if either str is too short or if len is string::npos).

#include<iostream>
#include<string>
using namespace std;int main()
{string s1("Hello,world!");string s2(s1,2,3);cout << s2 << endl;return 0;
}

二、string类对象的容量操作

在这里插入图片描述

函数名称功能说明
size(重点)返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty (重点)检测字符串释放为空串,是返回true,否则返回false
clear (重点)清空有效字符
reserve (重点)为字符串预留空间
resize (重点)将有效字符的个数该成n个,多出的空间用字符c填充

size与length

你可能会想,这两个方法都是返回字符串有效字符的长度,会不会有什么区别?

int main()
{string str1("hello world");cout << str1.size() << endl; // 11cout << str1.length() << endl; // 11return  0;
}

答案是没有

那为什么还会有两个同样作用的方法的存在呢?其实就是我说的,string是STL的前排兵,一开始想着要有个返回长度的方法,命名length也很合理,可是后面vector、map、set都是size,为了统一,只好也跟着加了个size

如果你问为什么不把length给删掉,其实你细想,删掉的话,已经用length这个方法写代码的个人、公司是不是又有意见了,所以一般我们只加不改,这就是向前兼容原则

一言以蔽之,length合理,size统一更规范

capacity

功能:返回当前对象所分配的存储空间的大小,一般情况下capacity返回大小中不包含’\0’

int main()
{string str1("hello world");cout << str1.size() << endl; // 11cout << str1.capacity() << endl; // 15return 0;
}

capacity返回值比size大

  std::string在底层上是属于动态数组,数组大小是不固定,根据实际需要进行调正,由于经常性出现频繁插入字符的清空,只存在size情况下,会导致频繁地向系统申请空间,性能降低,其实size和capacity的这种用法我们在之前也见识过
在这里插入图片描述

capacity的扩容机制

在这里插入图片描述
  我们会发现string类还是很智能的,能自动扩容,不需要我们操心,但是我们可以注意下扩容的机制,这其实跟编译器和指标因子的不同而有所差异

VS(msvc):扩容机制是第一次扩容到原来空间的两倍左右,之后则扩容当前空间的1.5倍
g++:扩容机制是以当前空间的两倍

empty

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

int main()
{string str1;if (str1.empty()) // 判断释放为空cout << "为空" << endl;elsecout << "非空" << endl;return 0;
}

clear

功能:清空string有效字符资源,不改变底层空间大小。影响有效元素size,不会影响空间容量大小capacity

int main()
{string str1("hello world");cout << str1.size() << endl; // 11cout << str1.capacity() << endl; // 15str1.clear(); // 清空有效字符cout << str1.size() << endl; // 0cout << str1.capacity() << endl; // 15return 0;
}

shrink_to_fit

功能:向系统请求字符串缩容到适合大小,但是该函数对于字符串的长度和内容是没有影响的,如果使用后容量并没有发生变化,那么可能字符串对象可能已经使用内存管理策略去避免频繁的内存分配和释放

int main()
{string str("hello world");cout << str.size() << endl; // 11cout << str.capacity() << endl; // 15cout << endl;str.resize(100);cout << str.size() << endl; // 100cout << str.capacity() << endl; // 111cout << endl;str.shrink_to_fit();cout << str.size() << endl; // 100cout << str.capacity() << endl; // 111cout << endl;return 0;
}

这个方法要少用,甚至其实我觉得都可以不用,因为我们一般认为缩容是释放部分空间从而达到正确大小,可是这是不对的,释放只能整个释放,事实上,正确的流程是,重新开一块空间,拷贝部分内容,进而释放原先的全部空间,损耗极大

reserve

功能:向系统申请预留空间,属于手动扩容

int main()
{string str1;cout << str1.capacity() << endl; // 15str1.reserve(100);cout << str1.capacity() << endl; // 111string str2(10, 'x');cout << str2.capacity() << endl; // 10str2.reserve(); // 缺省参数为0cout << str2.capacity() << endl; // 10return 0;
}

关于reserve与扩容的一些问题

  1. 既然我们前面说了编译器会自动扩容,为什么还要我们自己手动扩容呢?
    理由:扩容是需要付出代价的,如果是异地扩容,付出代价更大,需要进行空间开辟和数据拷贝,如果事先知道所需要的空间大小,使用reverse开辟足够使用的空间,减少频繁对内存的重分配,就算后期出现空间不足,也有自动扩容的机制,不需要担心大小是固定的。虽然自动扩容可以解决容量不足的情况,但是手段扩容可以减少频繁自动扩容的代价,属于一种优化手段
  2. reverse要求100个字节空间,但却开辟了111个字节空间呢?
    理由在不同编译器下机制是不同的,但是确保了至少满足所需空间。有些编译器开辟多个空间,是对reserve开辟的空间进行了二次开辟,可以灵活调用内存空间分配,在后继需要小空间,避免扩容
  3. reserve参数部分小于当前空间大小,提出申请空间请求,但是空间大小并没有发生改变
    理由:reserve进行扩容必须参数部分比当前空间大,才会改变string的底层空间总大小,否则就是无效扩容

resize

功能:改变字符串的实际长度
这里我们以"hello world"为例子来讲解三种不同情况
在这里插入图片描述

字符串变短(n>size)

int main()
{string str1("hello world"); // 长度为11cout << str1.size() << endl; // 11cout << str1.capacity() << endl; // 15str1.resize(2);cout << str1 << endl; // hecout << str1.size() << endl; // 2cout << str1.capacity() << endl; // 15return 0;
}

字符串在容量内变长(capacity>=n>size)

int main()
{string str1("hello world"); // 长度为11cout << str1.size() << endl; // 11cout << str1.capacity() << endl; // 15str1.resize(13);cout << str1 << endl; // hello worldcout << str1.size() << endl; // 13cout << str1.capacity() << endl; // 15return 0;
}

字符串修改长度超出容量(n>capcity)

int main()
{string str1("hello world"); // 长度为11cout << str1.size() << endl; // 11cout << str1.capacity() << endl; // 15str1.resize(50);cout << str1 << endl; // hello worldcout << str1.size() << endl; // 50cout << str1.capacity() << endl; // 63return 0;
}

  当resize修改长度超过capacity,capacity会进行自动扩容。至于最后capacity的值为什么不是50,在reserve中解释了不同编译器扩容机制是不同的

其中resize有两个重载,功能都是将字符串中有效字符个数改变到n个,不同点在于:
resize(size_t n):用’\0’来填充都出的元素空间
resize(size_t n,char c):用字符c来填充多出的元素空间

  一样的,相比之下我们鼓励用reserve,提前开好空间,避免频繁扩容

三、string类对象的访问

operator[ ]

在这里插入图片描述

我们只要了解第一个operator[ ]就行,其他都是为了规范性
我们不妨来看下这两个重载:
char& operator[ ] (size_t pos); // 读写
const char& operator[ ] (size_t pos) const; // 只读
其实,这里第一个重载体现了引用返回的一大用处:可供修改

// 相当于是自定义类型给数组化了,其实string还是蛮特殊的
// 这点我们在后面的学习会有更深的体会int main()
{string str1("hello world");for (int i = 0 ; i < str1.size() ; i++){// cout << str1.operator[](i) << endl;cout << str1[i] << endl;}const string str2("hello world");for (int i = 0 ; i < str2.size() ; i++){// str2[i]++; const修饰的话,没有修改的权限cout << str2[i] << endl;}return 0;
}

迭代器(简单介绍)

  迭代器(Iterator)是一种用于遍历容器(如列表、字典、集合等)元素的对象,它提供了一种统一的访问容器内部元素的方式,而不必暴露容器的具体实现细节。迭代器通常用于循环结构中,让程序员能够逐个访问容器中的元素,在讲解string类对象的遍历前,我想先简要讲解下这一概念

int main()
{string str1("hello world");string::iterator it = str1.begin();while (it != str1.end()) // 左闭右开{cout << *it << endl;it++;}return 0;
}

  在string里,你可以暂时把它当成是类似指针的东西

  我们有很多种遍历类对象的方式,但是迭代器才是主流。对于链表、树等数据结构,迭代器不在乎底层实现,是通用的遍历容器。迭代器是一种像指针的东西,他可以是指针也可以不是指针,具体还是看不同编译器的底层实现,迭代器有两种类型分别:可读可修改,可读不可修改,但是我们要注意存储迭代器的变量类型应该与容器的迭代器类型相匹配,以确保类型的一致性,避免编译器报错或者意外行为

反向迭代器

定义:string::reverse_iterator

int main()
{string str1("hello world");string::reverse_iterator rit = str1.rbegin();while (rit != str1.rend()){cout << *rit << "";++rit; // 请注意是++}cout << endl;return 0;
}

其实,这个反向迭代器的要求应该很少,我觉得正向的就可以满足所需了

四、string类对象的遍历操作

for+[ ]

前文已讲过这两个方法
// str.operator[ ](size_t pos)
// str[pos]

我前面也说了,这很像数组,所以我们很自然的写出这个遍历

int main()
{string str("Hello,world!");for (size_t i = 0; i < str.size(); i++) {cout << str[i]; // Hello,world!}cout << endl;return 0;
}

迭代器(begin(),end())

前文说,这很像指针,这样一提醒后该怎么遍历,也是很自然而然就能写出来

int main()
{string str("Hello,world!");string::iterator it1 = str.begin();while (it1 != str.end()) {cout << *it1;it1++;}cout << endl;return 0;
}

范围for

我们说范围for能够自动遍历所有元素,其实哪有什么自动化,范围for的底层还是迭代器,这点结论大家可自行查看汇编代码得出

int main()
{string str("Hello,world!");for (const auto& ch : str) {cout << ch;}cout << endl;return 0;
}

总结

  我真的要花大篇幅来讲string,这是因为其是我们学习STL的第一课,并且就像我在正文一再吐槽的,string设计的是真的冗余

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

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

相关文章

缓存穿透、击穿、雪崩及解决方案

在分布式系统中&#xff0c;尤其是使用缓存的系统中&#xff0c;缓存雪崩、缓存穿透和缓存击穿是常见的问题。下面详细说明这三者的定义和解决方案&#xff1a; 1. 缓存穿透 定义&#xff1a;缓存穿透是指请求的数据在缓存和数据库中都不存在。由于每次请求都会直接查询数据库…

Docekrfile和docker compose编写指南及注意事项

Dockerfile 基础语法 我们通过编写dockerfile,将每一层要做的事情使用语法固定下来&#xff0c;之后运行指令就可以通过docker来制作自己的镜像了。 构建镜像的指令&#xff1a;docker build /path -t imageName:tag 注意&#xff0c;docker build后的path必须是dockerfile…

阿里云对象存储OSS 速学

目录 1.创建一个Bucket 2.创建密钥AccessKey 3.在文档中心打开阿里云对象存储OSS 4.参考上传文件示例 以官网的文档为主&#xff0c;我的文章教学为辅 官网有详细的视频介绍&#xff1a; OSS快速入门_对象存储(OSS)-阿里云帮助中心 (aliyun.com)https://help.aliyun.com/…

25考研咨询周开启,西安电子科技大学是否改考408??

学长这几天帮大家问了西安电子科技大学是否会从833、834、953改考为408&#xff1f; 西电老师回复&#xff1a;根据上级文件要求&#xff0c;招生简章以及专业目录会在网上报名开始前公布&#xff0c;专业课不会又大变动&#xff01; 因为大家安心复习即可&#xff0c;保证今…

java解决跨域问题时的403报错

什么是跨域问题&#xff1f; 当一个请求的url的协议&#xff0c;域名&#xff0c;端口三者之间任意一个与当前页面url不同 即为跨域 问题背景&#xff1a; 如图&#xff0c;前端端口为8090&#xff0c;而后端端口为8099&#xff0c;形成跨域&#xff0c;无法对接 试图利用Spr…

【数据库】数据库课程设计mysql

数据库课程设计MySQL是一个涉及多方面内容的综合性项目&#xff0c;旨在通过实践加深对MySQL数据库的理解和应用。以下是一个基于MySQL的数据库课程设计概览&#xff0c;包括设计步骤、关键技术和操作示例。 一、数据库设计步骤 需求分析&#xff1a; 确定系统的功能需求和数据…

爬虫——爬取小音乐网站

爬虫有几部分功能&#xff1f;&#xff1f;&#xff1f; 1.发请求&#xff0c;获得网页源码 #1.和2是在一步的 发请求成功了之后就能直接获得网页源码 2.解析我们想要的数据 3.按照需求保存 注意&#xff1a;开始爬虫前&#xff0c;需要给其封装 headers {User-…

卫生间门口墙皮天天掉,是墙面“返潮”造成的?

业主说房子装修好5年了&#xff0c;卫生间防水出问题了&#xff0c;去现场看一下&#xff0c;看看能怎么处理。      到了工地以后&#xff0c;业主说卫生间门口两侧的墙皮都已经脱落了&#xff0c;天天往下掉&#xff0c;实在没办法了&#xff0c;就在墙上钉了几块纸壳子。…

linux基础指令的认识

在正式学习linux前&#xff0c;可以简单认识一下linux与win的区别 win&#xff1a;是图形界面&#xff0c;用户操作更简单&#xff1b;在刚开始win也是黑屏终端 指令操作&#xff0c;图形界面就是历史发展的结果。Linux&#xff1a;也存在图形界面比如desktop OS&#xff1b;但…

Ubuntu24.04桌面版下的网络管理

systemd-networkd组件 注意&#xff1a;systemd-network和NetworkManager组件两者冲突 使用systemd-networkd之前&#xff0c;需要先关闭其他网络管理器&#xff0c;如NetworkManager, netctl&#xff0c;dhcp daemon&#xff0c;dhcpcd, dhclient等 systemctl stop NetworkM…

【C++】模拟实现红黑树

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:实战项目集 ⚙️操作环境:Visual Studio 2022 目录 一.了解项目功能 二.逐步实现项目功能模块及其逻辑详解 &#x1f4cc;实现RBTreeNode类模板 &#x1f38f;构造RBTreeNode类成员变量 &#x1f38f;实现RBTreeNode类构…

Flutter modal_bottom_sheet 库:介绍与使用指南

Flutter的官方showModalBottomSheet方法虽然功能强大&#xff0c;但在某些场景下可能不够灵活。为了解决这个问题&#xff0c;社区提供了一个非常实用的第三方库——modal_bottom_sheet。这个库扩展了官方的ModalBottomSheet功能&#xff0c;提供了更多的自定义选项和更丰富的用…

异常场景分析

优质博文&#xff1a;IT-BLOG-CN 为了防止黑客从前台异常信息&#xff0c;对系统进行攻击。同时&#xff0c;为了提高用户体验&#xff0c;我们都会都抛出的异常进行拦截处理。 一、异常处理类 Java把异常当做是破坏正常流程的一个事件&#xff0c;当事件发生后&#xff0c;…

C 语言预处理详解:从宏替换到条件编译

目录&#xff1a; 前言1. 什么是预处理&#xff1f;2. 头文件展开3. 去注释4. 宏替换4.1 什么是宏&#xff1f;4.2 宏的作用范围4.3 使用宏的小Tips4.4 # 和 ##4.5 宏替换 vs 去注释 5. 条件编译5.1 什么是条件编译&#xff1f;5.2 条件编译的使用5.3 条件编译的作用 总结 前言…

什么是区块链桥?

什么是区块链桥&#xff1f; 区块链桥是一种实现资产从一个区块链转移至另一个区块链的工具&#xff0c;它解决了区块链技术中不同网络之间缺乏互操作性的问题。区块链桥通过创建代表另一区块链资产的合成衍生品&#xff0c;使得原本互不兼容的区块链资产能够相互连接和转移。…

Spring Boot新闻推荐:实时数据处理

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

JSR303微服务校验

一.创建idea 二.向pom.xml添加依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.7.RELEASE</version></parent><properties><java.vers…

【Git】Git在Unity中使用时的问题记录

个人向笔记。 &#xff08;为什么没截图&#xff0c;因为公司电脑没法截图&#xff01;&#xff09; 1 前言 主要记录在使用Git协同开发时的各种问题&#xff0c;方便以后查阅。 2 记录 2.1 合并冲突 git pull下来后直接给合并了&#xff0c;麻了。若不想直接合并应该先把分…

职业技术学校开设无人机培训技术详解

职业技术学校开设无人机培训技术&#xff0c;是一个涉及多个方面的综合性教学过程。以下是对该培训技术的详细解析&#xff1a; 一、培训目标 无人机培训技术的目标在于培养学员掌握无人机的基本原理、组装调试、飞行操作、安全规范及维修保养等技能&#xff0c;使其成为具备…

基于SSM的定制衣服系统的设计与实现(定制衣服管理平台的设计与开发、智慧服装定制系统的设计与实现、定制衣服管理系统的设计与实现(源码+定制+参考文档)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…