当前位置: 首页 > news >正文

c++ (8) string类

1、string的介绍

1.1学习string的意义

在我看来学习某个东西一定是要有意义的,那么这里string的作用和功能都相当的强大,对比起我们以前学习的c语言简直不知道方便了多少倍。

string类是因为它简化了字符串的操作,避免了传统字符数组的繁琐和潜在错误,提供了动态内存管理、丰富的成员函数(如查找、替换、拼接等),并支持与C++标准库的无缝集成,使得字符串处理更加高效、安全和便捷。

1.2C语言中的字符串

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列
的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户
自己管理,稍不留神可能还会越界访问。

1.3string类

从实际的角度上来说,string是不属于STL的范畴的,因为历史的缘故,string比STL更早出现,但是使用的时候我习惯于把它当做STL来理解。

它本质上是由 typedef而来 ,是由类模版basic_string<char>实例化而来,用来记录一些字符。     

 既然是一个类,那么它就有成员变量、成员函数,操作符重载和六大默认构造                                 接下来,将要从这几个角度思考问题。

2、string的默认函数

2.1构造函数

通过查表我们可以得到库里面给我提供了这些,我们一个一个来解析

string(): 空字符串构造函数(默认构造函数)

构造一个长度为零个字符的空字符串

string(const string&s)   拷贝构造,参数就是一个const string类

string(const char* s)

string(const char* s,size_t  n)   从 s 指向的字符数组中复制前 n 个字符

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

复制从字符位置 pos 开始并跨越 len 字符的 str 部分

 

npos

std::string::npos 是一个 std::string::size_type 类型的常量,表示一个无效的位置。它通常被定义为一个大整数 。

简单分析 size_t是无符号的数,-1的补码为  11111111111111111111111111111111,所以对于无符号数来说他的值为INT_MAX就是int类型最大值。

npos 是一个特殊的静态常量,表示一个无效的位置或“未找到”的值。它通常用于字符串操作中,表示某个操作没有找到预期的结果。

2.2析构函数

表中没有关于析构的实现,所以,我们不用注意这一点,毕竟在销毁时,它会自动调用,所以不必在意

3、运算符重载

3.1赋值运算符重载

这里库里面给了我们三个,其实就是调用我们上面的拷贝构造。

3.2流插入符与流输出符

<< >>使用这两个操作符,可以对string进行io流操作这个很简单

3.3 [ ] 的重载

返回值就是相应位置的元素,提高代码可读性。

3.4 + 的重载

直接在string后面加上后面那个string的元素,值得注意的是当目标对象空间不够的时候会自动扩容。

3.5关系的重载

1.等与不相等:两个字符串完全一样,字符数量,与字符一 一相等,不相等就是它的反向逻辑

“abcdef”  与 "abcdef"相等    “aaaaaa”与“bbbbbb”不相等

2.大于 :第一个字符串大于第二个字符串:两个字符串中,第一个不相等的字符,左边的字符大于右边字符的Ascll值,还一种情况是,左右两边的字符串Ascll值一直相等,但是,左边字符串更长

"abcdef"大于"abcdea"  f的Ascll值大于a

“ abcde”大于" abcd"  左边字符串比右边长

其实介绍完这两个,其他的就不用介绍了

3. 小于:不就是 大于等于取反吗?

4.大于等于:不就是大于和等于或一下

5.小于等于:就是大于取反


4、内部数据查看的函数

4.1  size和length

这里size和length的作用是一样的,都是返回元素的个数。因为历史原因,string问世的时候还没STL,所以先有的length,后来出现STL为了统一出现了size。

4.2 max_size就是返回最大值

4.3 capacity就是容量。

4.4 clear就是清理string

4.5 reserve是扩容

需要注意的是如果输入的空间比原来得大,那就一定会扩容,但是如果小与的话就不一定会缩容。

reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参
数小于string的底层空间总大小时,reserver不会改变容量大小。

5、string的增删查改

5.1 append

​(1) string
附加 str 的副本。
(2) substring
附加 str 的子字符串的副本。substring 是 str 的一部分,从字符位置 subpos 开始并跨越 sublen 字符(或者直到 str 的末尾,如果 str 太短或 sublen 为 string::npos)。
(3) c-string
追加由 s 指向的以 null 结尾的字符序列(C 字符串)形成的字符串的副本。
(4) buffer
在 s 指向的字符数组中追加前 n 个字符的副本。
(5) fill
追加字符 c 的 n 个连续副本。
(6) range
按相同的顺序附加范围 [first,last) 中的字符序列的副本。
(7) 初始化器列表
按相同的顺序附加 il 中每个字符的副本。

5.2 尾插push_back()

尾插就顾名思义喽,比较简单就不做解释了。

5.3替换assign

具体功能就是

示例:

5.4 插入insert()

这里提供了很多种方法,但实际我们只要掌握几种就好了,之后如果遇到不会的可以查表解决。

值得注意的是,insert的时间复杂度是O(n)。

 

5.5 删除erase()

可以传要删除的位置和长度,也可以传迭代器。

5.6 替换replace()

示例:

6、迭代器遍历string

6.1  迭代器

迭代器类似于指针,指针只是指向一个变量,但是迭代器是指向一个自定义类型,他们都是用来访问内容的,下面就可以使用迭代器去遍历string类

直接看文档,一共八个迭代器

先看begin和end

他们是一个获取首位置,以获取尾位置,对于end来说它获取的位置为有效位的下一个

end: Returns an iterator pointing to the past-the-end character of the string.

看代码如何遍历:

再看  rbegin() 以及  rend()

rbegin()是返回最后一个有效位置,而rend是返回第一个有效位置之前的位置。注意他们是反向迭代器,所以移动的方向也会反向。

那么看代码吧!!

再看 cbegin() cend(),他们是常量迭代器,不能修改字符串的内容。当然前面的非常量迭代器是可以修改string的。我们这里长话短说

string ::const_iterator it=a.cbegin();

同理crend() 以及  crbegin()的道理是一样的,他们只是反向的常量迭代器

string ::const_reverse_iterator it=a.crbegin();

6.2 auto和范围for

auto关键字

在这里补充 2 C++11 的小语法,方便我们后面的学习。
在早期 C/C++ auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量,后来这个
不重要了。 C++11 中,标准委员会变废为宝赋予了 auto 全新的含义即: auto 不再是一个存储类型
指示符,而是作为一个新的类型指示符来指示编译器, auto 声明的变量必须由编译器在编译时期
推导而得
auto 声明指针类型时,用 auto auto* 没有任何区别,但用 auto 声明引用类型时则必须加 &
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际
只对第一个类型进行推导,然后用推导出来的类型定义其他变量
auto 不能作为函数的参数,可以做返回值,但是建议谨慎使用。
auto 不能直接用来声明数组。
#include<iostream>
using namespace std;
int func1()
{
	return 10;
}
// 不能做参数
void func2(auto a)
{}
// 可以做返回值,但是建议谨慎使用
auto func3()
{
	return 3;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = func1();
	// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
	auto e;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	int x = 10;
	auto y = &x;
	auto* z = &x;
	auto& m = x;
	cout << typeid(x).name() << endl;
	cout << typeid(y).name() << endl;
	cout << typeid(z).name() << endl;
	auto aa = 1, bb = 2;
	// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
	auto cc = 3, dd = 4.0;
	// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
	auto array[] = { 4, 5, 6 };
	return 0;
}

那auto在这里好像没有明显的作用哈,那么我们看看当类名很复杂的情况下,它的作用就明显了:

#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange",
"橙子" }, {"pear","梨"} };
// auto的用武之地
//std::map<std::string, std::string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}

范围for

对于一个 有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此
C++11 中引入了基于范围的 for 循环。 for 循环后的括号由冒号 分为两部分:第一部分是范围
内用于迭代的变量,第二部分则表示被迭代的范围 ,自动迭代,自动取数据,自动判断结束。
范围 for 可以作用到数组和容器对象上进行遍历。
范围 for 的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
int main()
{
for (auto& e : array)
e *= 2;
for (auto e : array)
cout << e << " " << endl;
string str("hello world");
for (auto ch : str)
{
cout << ch << " ";
}
cout << endl;
return 0;
}

范围for有三个自动:

1、自动赋值给e

2、自动array++

3、自动判断停止

全是自动,看起来很高级哈,但其实底层就是迭代器,如果自己定义一个类不写迭代器就会报错

如果要修改数据可以这样:

for(auto& e : array)

相关文章:

  • 灵茶山艾府基础算法精讲
  • 享元模式(Flyweight Pattern)
  • Federated learning client selection algorithm based on gradient similarity阅读
  • 《鸿蒙携手AI:解锁智慧出行底层逻辑》
  • 高速工业相机的核心特点及多领域应用
  • Java中抽象类和接口
  • 详解堆排序(超详细)
  • AI Tokenization
  • Docker 镜像构建与优化
  • 修形还是需要再研究一下
  • Maven中为什么有些依赖不用引入版本号
  • 尝试在软考61天前开始成为软件设计师-数据结构算法
  • 内核编程十:进程的虚拟地址空间
  • Docker Hub Mirror 终极解决方案——0成本,超高速!
  • RAG优化:python从零实现时间管理大师Self-RAG
  • 红宝书第十二讲:详解JavaScript中的工厂模式与原型模式等各种设计模式
  • 第八章:防火墙
  • [实操]Mysql8 InnoDB引擎主从配置三节点操作流程
  • 论文阅读笔记:Denoising Diffusion Implicit Models
  • 消息队列ActiveMQ、RabbitMQ、RocketMQ、Kafka对比分析和选型
  • 新型算法助力听障人士听得更清晰
  • 江西德安回应“义门陈遗址建筑被没收”:将交由规范的义门陈相关社会组织管理
  • 企业取消“大小周”引热议,半月谈:不能将显性加班变为隐性加班
  • 中公教育薪酬透视:董监高合计涨薪122万,员工精简近三成
  • 影子调查丨危房之下,百余住户搬离梦嘉商贸楼
  • 偷拍拷贝某轨道车技术信息后撰写论文发表,工程师被判一年有期徒刑