【C++初阶】:STL——String从入门到应用完全指南(1)

🎈主页传送门**:良木生香**

🔥个人专栏:《C语言》 《数据结构-初阶》 《程序设计》《鼠鼠的C++学习之路》

🌟人为善,福随未至,祸已远行;人为恶,祸虽未至,福已远离



前言:在上一篇文章中我们已经学习了STL的组成部分以及编码的又来(即为什么要学习String),那么这篇文章就是正式进入STL的学习了,在String部分我将会分成大概四篇文章的版图进行讲解,话不多说,我们直接开始吧


目录

一、String类

二、String的构造:

2.1、defualt构造方法

2.2、copy拷贝构造

2.3、使用substring子字符串构造

2.4、使用C_string字符串构造

[2.5、根据序列构造(from sequence)](#2.5、根据序列构造(from sequence))

三、string的遍历与修改:

3.1、下标+[]访问

3.2、at访问

3.3、越界检查

3.4、size()与length()

3.5、迭代器

3.5.1、begin()与end()

3.5.2、迭代器

3.5.3、反向迭代器

3.5.4、迭代器的作用

四、C++11的一些小特性:

4.1、auto语法

4.2、范围for


一、String类

根据String的定义,我们可以知道,String是由basic_string重命名而来的:

二、String的构造:

String的构造有以下几种方式:

我们会重点讲解1,2,3,4,5点的构造方法:

2.1、defualt构造方法

defualt就是string的默认构造,将string实例化出的字符串构造成空:

cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

int main() {
	string s;
	if (s == "") {
		cout << "s is nullptr" << endl;
	}
	return 0;
}

运行结果为:

2.2、copy拷贝构造

copy顾名思义就是将已有的string字符串用来初始化另一刚创建的字符串:

2.3、使用substring子字符串构造

对于文档中的

复制代码
string (const string& str, size_t pos, size_t len = npos);

为什么在size_t len = npos这个东西呢?官方给出的答案是这样的:

就算输入的len已经超过了该字符串的范围,但是依旧不会有问题。

2.4、使用C_string字符串构造

我们知道,string的诞生就是为了解决C语言中char类型字符串的不足,所以自然是兼容C语言的字符串,我们就可以用C语言的字符串初始化string类型:

2.5、根据序列构造(from sequence)

即从C语言风格的字符串中取前n个字符对string进行初始化

与前面的substring不同的是,这个只能取前n个字符,不能从任意地方开始取字符串。

三、string的遍历与修改:

3.1、下标+[]访问

我们可以使用size()函数来返回string字符串的长度,再根据这个长度对字符串进行遍历:

cpp 复制代码
#include<iostream>
using namespace std;

int main() {
	string s = "hello world";
	size_t size_string = s.size();
	cout << size_string << endl;
	for (int i = 0; i < size_string; i++) {
		cout << s[i] << " ";
	}
	return 0;
}

运行结果如下:

因为string的底层说实话还是数组,所以可以用**[]+下标**的方式对元素进行访问。

同时我们可以看到,operator[]的返回值是引用返回,这就导致了它还有有一个特性:可以修改返回值:

在s[0]中,返回的是'h',同时被修改成'x'。

3.2、at访问

除了下标+[]访问的形式,在string中还有一种名为 at 的访问形式,是这么用的:

cpp 复制代码
#include<iostream>
using namespace std;

int main() {
	string s = "he is a good man";
	char ch = s.at(10);
	cout << ch << endl;
	return 0;
}

其返回值是pos位置的引用,运行结果如下:

效果与下标+[]一致。

3.3、越界检查

在string中有严格的越界访问检查:

cpp 复制代码
#include<iostream>
using namespace std;

int main() {
	string s = "hello world";
	s[100] = 'x';
	cout << s << endl;
	return 0;
}

当我们使用下标+[]越界访问时就会触发string的断言。

但是如果用at()函数,那么触发的就不是断言,而是抛出错误:

这两者有这个差异,可以留意一下。

3.4、size()与length()

在对string进行求长度的时候,通常会看到这两种方式,很多人会疑惑为什么要有两种 ?有length()不就够用了吗?

其实在早期的时候,确实是使用ength()更多一点,但是后来STL不断完善,接口也变得更加的丰富,size()的使用场景就更多了,总而言之,length()几乎是只给string使用,但是size()能适用于u所有容器,因为string本身就是一种容器。

两者在计算string长度大小时都不会带上'\0',因为'\0'只是字符串结束的标志,属于无效字符。

3.5、迭代器

在容器中有这么一个东西,叫做迭代器,我们可以将它理解成行为像指针一样的东西,其底层确实也是由指针实现的,但是还有有很多地方与指针不相同,在string类中的迭代器有以下几种:

我们一组一组来讲:

3.5.1、begin()与end()

begin()指向的是string的第一个字符,而end()指向的是最后一个字符的下一个位置,属于左闭右开的特性。那这跟迭代器有什么关系呢?

3.5.2、迭代器

string中的迭代器是这样的:string::iterators

使用时就是这样子的:

cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

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

运行结果:

总的来说,迭代器不等价于指针,但是像指针,也是由指针作为底层实现的。

在begin()和end()的定义中我们也可以看到迭代器是有const(string::cosnt_iterator) 和 非const两种的,就是用于const string和非cosnt string.

3.5.3、反向迭代器

顾名思义,反向迭代器就是将头和尾进行对调,使用方法如下:

cpp 复制代码
#include<iostream>
using namespace std;

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

运行结果如下:

代码中的rit++其实就是从后往前遍历,如写成rit就会出现错误。

3.5.4、迭代器的作用

迭代器的作用就跟指针类似,那我为什么不直接使用指针?迭代器有什么作用?

1、提供统一的遍历和修改容器的方式,这样就能保证多个容器使用的时候效率能够提高。

2、有了迭代器,在使用STL中算法就是实现泛型编程,即在对数据及进行遍历的时候能够有统一的方式,实现STL高性能运算的能力。

对于一下这几种迭代器的接口,就是针对const类型的。了解就行。

四、C++11的一些小特性:

4.1、auto语法

auto关键字:通过初始化的类型能够自主推导对象类型。

1、在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

2、用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

3、当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

4、auto不能作为函数的参数,可以做返回值,但是建议谨慎使用

5、auto不能直接用来声明数组

有什么用呢?像在刚才写的string::iterator就可以写成:

cpp 复制代码
#include<iostream>
using namespace std;

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

这时候编译器就能自动推断出it 的类型是string::iterator类型.

但是这就带来了一个弊端:降低了程序的了可读性。

同时,auto也可以是指针:

cpp 复制代码
#include<iostream>
using namespace std;

int main() {
	int i = 10;
	
	auto p1 = &i;
	auto* p2 = &i;	//指定只能存放地址
	auto& R1 = i;	//R1是对i的引用
	auto R2 = R1;	//将R1的值赋给R2
	auto& R3 = R1;

	cout <<"i:"<< i << endl;
	cout <<"p1:"<< p1 << endl;
	cout <<"p2:"<< p2 << endl;
	cout <<"&R1:"<< & R1 << endl;
	cout <<"&R2:"<< & R2 << endl;
	cout <<"&R3:"<< & R3 << endl;

	return 0;
}

运行结果如下:

4.2、范围for

C++11中提供了一种比较新颖的遍历方式:范围for,它能够自动对容器进行遍历:

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

像代码中的auto e : s,这时候编译器就会自动取容器值,自动++,碰到结尾自动结束。

如果我们尝试对它的值进行运算呢?比如++之类的,也是可以的但是推荐最好是用上&

其实范围for的底层就是迭代器,这就可以让支持迭代器的容器都能使用范围for,数组也支持,因为用指针遍历和用迭代器遍历是同一个原理:

范围for就是一个语法糖,因为用起来非常的方便。

以上呢,我们讲了三种别遍历string类的方式:

  1. 下标+[ ];
  2. 迭代器
  3. 范围for

那么以上就是本次所有的内容了

文章是自己写的哈,有什么描述不对的、不恰当的地方,恳请大佬指正,看到后会第一时间修改,感谢您的阅读~~~~


博主手写笔记:


相关推荐
Bug 挖掘机2 小时前
一篇理清Prompt,Skill,MCP之间的区别
开发语言·软件测试·python·功能测试·测试开发·prompt·ai测试
XWalnut2 小时前
LeetCode刷题 day16
数据结构·算法·leetcode·链表·动态规划
寒秋花开曾相惜2 小时前
(学习笔记)4.1 Y86-64指令集体系结构(4.1.4 Y86-64异常&4.1.5 Y86-64程序)
开发语言·jvm·数据结构·笔记·学习
Kurisu_红莉栖3 小时前
c++复习——const,static字
c++
码界筑梦坊3 小时前
302-基于Python的安卓应用市场数据可视化分析推荐系统
开发语言·python·信息可视化·毕业设计·fastapi
czxyvX3 小时前
1-Qt概述
c++·qt
齐鲁大虾3 小时前
新人编程语言选择指南
javascript·c++·python·c#
Eyfcom3 小时前
快递管理系统:从“功能实现”到“人性化体验”与“客户洞察”的技术跃迁
c语言·系统架构·快递管理系统
LiLiYuan.3 小时前
【Java 6种线程状态】
java·开发语言