STL简介 + string【上】

news2025/5/17 14:03:40

 一 . STL简介

1.1 什么是STL

STL(standard template libaray - 标准模板库)  : 是C++标准库的重要组成部分 , 不仅是一个可复用的组件库 , 而且是一个包罗  数据结构  与  算法  的软件框架 。 

注意 : 是标准库的一部分 !C++标准库还包括其他的库 ,比方如下:

 1.2 STL的版本

  • 原始版本

Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许

任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原

始版本一样做开源使用。 HP 版本--所有STL实现版本的始祖。

做一个小的知识扩展:(比较出名的闭源和开源有)

闭源 : windows   mac  os , Oracle

开源 :  linux  git

  • P. J. 版本

由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。

  • RW版本

由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一

般。

  • SGI版本

由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,

移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。我们后面学习

STL要阅读部分源代码,主要参考的就是这个版本。

1.3 STL的六大组件

1.4 STL的重要性

网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层 的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。

1.5 如何学习STL

学习STL的三个境界:能用,明理,能扩展

二 . 标准库中的string 类

注 :这里会比较详细的介绍string 类 , 以及文档怎样详细阅读 。记住 , 死记硬背是不可取的 , 理解并且熟悉使用才是根本 , 不懂的时候 , 可以在文档里查找。

2.1 string 类(了解)

string 类的文档介绍 :string - C++ Reference

在使用string 类时 , 必须包含 #include 头文件以及 using namespace std;

2.2 auto 和 范围 for

这里补充两个C++的小语法 , 方便我们后续的学习 。

auto 关键字

  • 早期C/C++中的auto 的含义是 : 使用auto修饰的变量 , 是具有自动存储器的局部变量 , 后来这个不重要了 。 C++11中 ,标准委员会变废为宝赋予了auto 全新的含义 即:auto 不再是一个存储类型的指示符 , 而是作为一个新的类型指示符来指示编译器 , auto 声明的变量必须又编译器在编译时期推导而得
  • 用auto 声明指针类型时 , 用auto 和 auto* 没有任何区别 , 但用auto 声明引用类型时则必须加 & 
  • 当在同一行声明多个变量时 , 这些变量必须是相同类型 , 否则编译器将会报错 , 因为编译器实际只对第一个类型进行推导 。 然后用推导出来的类型定义其他变量 。
  • auto 不能作为函数的参数(后面一点的语法会支持) , 可以做返回值 , 但是建议谨慎使用
  • auto   不能  直接用来声明数组

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;


//auto -- 自动推导类型
int func1()
{
	return 1;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = func1();

	//编译报错: error C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
	//auto e;

	int x = 10;
	auto y = &x;

	//右边必须是指针
	auto* z = &x;
	auto& k = x;

	auto aa = 1, bb = 2;
	//编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
	//auto cc = 1,dd = 2.0;

	//编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
	//auto a[] = { 1,2,3 };

	return 0;
}

 一般内置类型直接写就好了,没必要转化为  auto  , 那么时候使用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;
	}
	return 0;
}

 auto 做返回值,层层推导的时候 很不方便 ,要么就把注释写清楚 ,提高代码的可读性 ,下面代码如果想要知道 ret 是什么类型的 ,要经过 fun3 -> func2 ->func1 , 层层推导出 int , 如果代码很长 , 就不方便阅读代码 

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

//auto 做返回类型层层推导的时候,很不方便...
int func1()
{
	return 1;
}
auto func2()
{
	return func1();
}
auto func3()
{
	return func2();
}
int main()
{
	auto ret = func3();
	return 0;
}

范围 for (语法糖)

在学习中 , 会听过类似语法糖的词汇 , 就是某个语法 用起来很 “甜” , 很方便 , 玩起来很开心

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

#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	// C++98的遍历
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
	{
		array[i] *= 2;
	}
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
	{
		cout << array[i] << " ";
	}
	cout << endl;

	//范围for
	//语法糖:自动++,自动判断,自动执行
	for (auto & e : array)
	{
		e *= 2;
	}
	for (auto e : array)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

2.3 string 类的常用接口说明  

通过文档 , 我们发现string 没在containers , 这是由于历史的原因导致的 , string 出现比STL早 , 但是string 的功能角度看 , 可以把string 归纳到 containers

我们点开string 来看 , 发现string 实际上是basic_string 类被 typedef  , 这里重点学string , 因为接口的高度相似,并且用的最多的是string , 因为string 方便存储在utf8

 

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

int main()
{
	cout << sizeof(char) << endl;
	cout << sizeof(wchar_t) << endl;
	cout << sizeof(char16_t) << endl;
	cout << sizeof(char32_t) << endl;
	return 0;
}

 1 . string 类对象的常见构造 

string::string - C++ Reference

析构的话 , 底层会自动释放,自动调用

string 的构造有很多 , 需要记住是无参构造,有参构造,拷贝构造,其他的了解即可,使用时忘记随时查阅文档 , 无需刻意记忆 , 多练

2 . string类对象的容量操作 

 注意 :

1 . size() 与 length()  : 方法底层实现原理完全相同 , 引入size()的原因 是为了与其他容器的接口保持一致 一般情况下基本都是用size()。 

2 .  max_size() : 没什么实际意义 ,因为实际中开不了这么大的空间 。

3 . clear() : 只是将string中有效字符清空 , 不改变底层空间大小

4 . capacity() : 返回容量 , 不包含'/0';

 5 . shrink_to_fit : 

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

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <string>

void Test_String1()
{
	string s("hello world!");
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	//测试reserve是否会改变string中有效元素个数
	s.reserve(100);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	//测试reserve参数小于string的底层空间大小时,是否会将空间缩小
	s.reserve(5);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	//利用reserve提高插入数据的效率,避免增容带来的开销
}
int main()
{
	Test_String1();
	return 0;
}

7 . resize(size_t n ) 与 resize(size n , char c) : 都是将字符串有效字符个数改变到 n 个 , 不同的是当字符个数增多时 , resize(n) 用 0 来填充多出的元素空间 , resize(size_t  n , char c)用字符 c 来填充多出的元素空间 。 注意resize在改变元素个数时 , 如果时将元素个数增多 , 可能会改变底层容量的大小 , 如果将元素个数减少 , 底层空间总大小不变 。  

void Test_String2()
{
	string s("hello world!");
	cout << "size:"<< s.size() << endl;
	cout << "capacity:"<<s.capacity() << endl;
	cout << "s:" << s << endl;
	cout << endl;

	//将s中的字符串清空,注意清空时只是将size清0,不改变底层空间
	s.clear();
	cout << "size:" << s.size() << endl;
	cout << "capacity:" << s.capacity() << endl;
	cout << "s:" << s << endl;
	cout  << endl;

	//将s中有效字符个数增加到10个,多出位置用'a'进行填充
	s.resize(10, 'a');
	cout << "size:" << s.size() << endl;
	cout << "capacity:" << s.capacity() << endl;
	cout << "s:" << s << endl;
	cout << endl;

	//将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行补充
	s.resize(15);
	cout << "size:" << s.size() << endl;
	cout << "capacity:" << s.capacity() << endl;
	cout << "s:" << s << endl;
	cout << endl;

	//将s中有效字符个数缩小到5个
	s.resize(5);
	cout << "size:" << s.size() << endl;
	cout << "capacity:" << s.capacity() << endl;
	cout << "s:" << s << endl;
	cout << endl;
}

3. string类对象的访问  及  遍历  操作 

1 )string 类访问对象 : 使用 [] , 或者at

不同点就是  访问失败  的时候返回形式不同 ,at 访问失败会抛异常(程序还会继续跑,比较温和的方式) , [] 访问失败 , 断言(直接结束程序运行 , 比较暴力的方式) 

2 ) 遍历string 对象的方式 :

1 . 下标 + [ ] : 运算符重载operaror[]  

2 . 迭代器

3 . 范围 for

先来看 下标 + [] 的遍历方式 :  

#include <string>
int main()
{
	//无参的构造
	string st1;

	//带参的构造
	string st2("Hello World!");

	//拷贝构造
	string st3(st2);

	string st4(st2, 6, 1000);

	cout << st1 << endl;
	cout << st2 << endl;
	cout << st3 << endl;
	cout << st4 << endl;
	//1.下标+[] 
	for (size_t i = 0; i < st2.size(); i++)
	{
		st2[i] += 1;
	}
	for (size_t i = 0; i < st2.size(); i++)
	{
		cout << st2[i] << " ";
	}
	cout << endl;
	return 0;
}

实际上是重载了[] 运算符 , 能够使string 能像数组一样被访问 , 底层的operator[] 如下 :

namespace bit
{
	class string
	{
	public:
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];

		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

这里我们就能深刻体会到  引用&作为返回值  的意义 :

再来看  迭代器遍历 :

	//2 .迭代器
	// [ )
	string::iterator it = st2.begin();
	while (it != st2.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

思考 :  为什么有了 下标 + [] 的遍历方式还需要有  迭代器  呢 ? 前者明明用到更顺。

 因为下标 + [] 限制底层必须是数组,像链表就被限制了 , 但迭代器是通用的!

 倒着遍历 : 反向迭代器(链表不一定有反向迭代器)

	string::reverse_iterator rit = st2.rbegin();
	while (rit != st2.rend())
	{
		cout << *rit << endl;
		++rit;
	}
	cout << endl;

迭代器有四种 :

 范围for 遍历 :

	//范围for -- 底层是迭代器
	for (auto ch : st2)
	{
		cout << ch << " ";
	}
	cout << endl;

编译器编译  范围for 的时候 , 替换为迭代器 。

所以从上层看有三种遍历方式:下标+[] , 迭代器,返回for , 底层就只有两种遍方式 : 下标+[] 和迭代器! 

4. string类对象的修改操作

1 )   push_back() / append() / opeartor+=()  都是在 字符串后追加  (尾插)

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

#include <string>
void Test_string1()
{
	string s1;
	cout << s1.max_size() << endl;
}
void Test_string2()
{
	string s1;
	string s2("hello world");

	cout << s2.size() << endl;
	cout << s2.capacity() << endl;
}
void Test_string3()
{
	string s1;
	s1.push_back('x');
	s1.push_back('x');
	s1.push_back('x');

	s1.append("yyy");

	string s2("hello world");
	s1.append(s2.begin(), s2.end());
	//迭代区间可以取字符串的一部分
	s1.append(s2.begin() + 6, s2.end());
	cout << s1 << endl;

	//实际上,push_back与append不常用,+=最常用
	//类似c的 strcat , 1)从字符串开始找到'\0'后再追加
	//2)不会对原始空间扩容,追加会比较容易越界
	s1 += ' ';
	s1 += "zzz";
	s1 += s2;
	cout << s1 << endl;

}

int main()
{
	//Test_string1();
	//Test_string2();
	Test_string3();
	return 0;
}

 2)   insert 从某个位置开始插入 :  谨慎使用,头插数据时候 , 后面的数据需要全部往后挪

void Test_String3()
{
	string s("hello world!");
	s.insert(0, "xxx,");
	cout << "s:" << s << endl;

	s.insert(0, 1, 'a');
	cout << "s:" << s << endl;

}

 注意 : 

1 . 在string 尾部追加字符时 , s.push_back(c) / s.append(1,c) /s+='c‘三种的实现方式差不多,一般情况下string类的+=操作用的比较多 , +=操作不仅可以连接单个字符 , 还可以连接字符串 。

2 . 对string 操作时 , 如果能够预估到放多少个字符 , 可以先通过 reserve 把空间预留好

3 ) erase: 删除数据

void Test_String4()
{
	string s("hello world!");

	//从第0个位置,删除一个字符
	s.erase(0, 1);
	cout << "s:" << s << endl;

	//头删一个数据
	s.erase(s.begin());
	cout << "s:" << s << endl;

	//不传参数时,默认从头开始,删完
	s.erase();
	cout << "s:" << s << endl;
}

5. string类非成员函数 

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

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

相关文章

【Bluedroid】A2DP Sink播放流程源码分析(二)

接上一篇继续分析:【Bluedroid】A2DP Sink播放流程源码分析(一)_安卓a2dp sink播放流程-CSDN博客 AVDTP接收端(Sink)流事件处理 bta_av_sink_data_cback 是 Bluedroid 中 A2DP Sink 角色的 AVDTP 数据回调函数,负责处理接收端的音频数据事件,将底层接收到的音频数据传递…

redis利用备忘录

fofa: icon_hash"864611937" 防护&#xff1a; redis的安全设置&#xff1a;设置完毕&#xff0c;需要重加载配置文件启动redis 1.绑定内网ip地址进行访问 2. requirepass设置redis密码 3.保护模式开启protected-mode开启&#xff08;默认开启&#xff09; 4.最好把…

SAP系统中MD01与MD02区别

知识点普及&#xff0d;MD01与MD02区别 1、从日常业务中&#xff0c;我们都容易知道MD01是运行全部物料&#xff0c;MD02是运行单个物料 2、在做配置测试中&#xff0c;也出现过MD02可以跑出物料&#xff0c;但是MD01跑不出的情况。 3、MD01与MD02的差异: 3.1、只要在物料主数…

ubuntu学习day3

3 编译与调试 3.1 gcc/g编译器 当我们进行编译的时候&#xff0c;要使用一系列的工具&#xff0c;我们称之为工具链。SDK就是编译工具链的简写&#xff0c;我们所使用的是gcc系列编译工具链。使用-v参数来查看gcc的版本&#xff0c;从而确定某些语法特性是否可用&#xff0c;…

6.8.最小生成树

一.复习&#xff1a; 1.生成树&#xff1a; 对于一个连通的无向图&#xff0c;假设图中有n个顶点&#xff0c;如果能找到一个符合以下要求的子图&#xff1a; 子图中包含图中所有的顶点&#xff0c;同时各个顶点保持连通&#xff0c; 而且子图的边的数量只有n-1条&#xff0…

QT中栅格模式探索

1、Qt中选择了栅格模式&#xff0c;如下图所示&#xff1a; 2、在进行整个大的UI界面布局时&#xff0c;需了解每个控件所需要选择的属性sizePolicy。 sizePolicy包含如下几种选择&#xff1a; 3、举个例子&#xff1a;此时整个UI界面&#xff0c;我采用了栅格模式&#xf…

C++入门基础:命名空间,缺省参数,函数重载,输入输出

命名空间&#xff1a; C语言是基于C语言的&#xff0c;融入了面向对象编程思想&#xff0c;有了很多有用的库&#xff0c;所以接下来我们将学习C如何优化C语言的不足的。 在C/C语言实践中&#xff0c;在全局作用域中变量&#xff0c;函数&#xff0c;类会有很多&#xff0c;这…

tomcat 的安装与启动

文章目录 tomcat 服务器安装启动本地Tomcat服务器 tomcat 服务器安装 https://tomcat.apache.org/下载 Tomcat 10.0.X 启动本地Tomcat服务器 进入 Tomcat 的 bin

算法-堆+单调栈

堆 首先堆在我们的Java中我们的是一个优先队列类 PriorityQueue 然后我们要弄最大堆和最小堆 最大堆&#xff1a; PriorityQueue<Integer> pq new PriorityQueue<Integer>((a, b) -> b - a); 最小堆&#xff1a; PriorityQueue<Integer> pq new P…

物联网平台管理系统

物联网平台管理系统概述 物联网平台管理系统是物联网架构中的核心枢纽&#xff0c;承担着承上启下的关键作用。它向下连接各类物联网设备&#xff0c;实现设备的接入、管理与控制&#xff1b;向上为应用开发提供统一的数据接口和共性模块工具&#xff0c;支撑起各种丰富多彩的…

STM32CubeMX-H7-15-SPI通信协议读写W25Q64

前言 SPI&#xff08;Serial Peripheral Interface&#xff09;通信协议是一种高速、全双工、同步的串行通信协议 本篇文章就使用W25Q64模块来学习SPI,包括软件SPI代码的编写&#xff0c;硬件SPI&#xff0c;中断SPI和DMASPI SPI的应用场景和模块 &#xff01;这里是抄AI的&a…

【软考】论devops在企业信息系统开发中的应用

摘要&#xff1a; 随着互联网的不断发展&#xff0c;各行各业都在建设自己的企业信息系统&#xff0c;而随着业务的不断升级和复杂化&#xff0c;系统的更新迭代速度越来越快&#xff0c;系统也越来越复杂。对于信息系统开发者&#xff0c;架构师&#xff0c;管理者&#xff0c…

生物化学笔记:医学免疫学原理22 肿瘤及肿瘤治疗

肿瘤及肿瘤治疗 免疫疗法 CAR-T细胞介绍

JVM考古现场(二十二):降维打击·用二向箔优化内存模型

"警报&#xff01;三维堆内存正在经历二维化坍缩&#xff01;" 我腰间的玄铁令突然震动&#xff0c;在蜀山剑派的量子剑阵中投射出诡异的曼德博分形——这是三体文明发动降维打击的铁证&#xff01; 楔子&#xff1a;二向箔奇点降临 昆仑镜监控日志&#xff1a; // …

操作系统-PV

&#x1f9e0; 背景&#xff1a;为什么会有 PV&#xff1f; 类比&#xff1a;内存&#xff08;生产者&#xff09; 和 CPU&#xff08;消费者&#xff09; 内存 / IO / 磁盘 / 网络下载 → 不断“生产数据” 例如&#xff1a;读取文件、下载视频、从数据库加载信息 CPU → 负…

医院数据中心智能化数据上报与调数机制设计

针对医院数据中心的智能化数据上报与调数机制设计,需兼顾数据安全性、效率性、合规性及智能化能力。以下为系统性设计方案,分为核心模块、技术架构和关键流程三部分: 一、核心模块设计 1. 数据上报模块 子模块功能描述多源接入层对接HIS/LIS/PACS/EMR等异构系统,支持API/E…

【MATLAB代码例程】AOA与TOA结合的高精度平面地位,适用于四个基站的情况,附完整的代码

本代码实现了一种基于到达角(AOA) 和到达时间(TOA) 的混合定位算法,适用于二维平面内移动或静止目标的定位。通过4个基站的协同测量,结合最小二乘法和几何解算,能够有效估计目标位置,并支持噪声模拟、误差分析和可视化输出。适用于室内定位、无人机导航、工业监测等场景…

PC主板及CPU ID 信息、笔记本电脑唯一 MAC地址获取

&#x1f947; 版权: 本文由【墨理学AI】原创首发、各位读者大大、敬请查阅、感谢三连 &#x1f389; 声明: 作为全网 AI 领域 干货最多的博主之一&#xff0c;❤️ 不负光阴不负卿 ❤️ 文章目录 PC主板及CPU ID 信息物理 MAC地址获取win11 新电脑 wmic 安装❤️ 欢迎一起学AI…

C++17 信号量模拟实现

C17 信号量模拟实现 一、实现原理 C17 标准库没有原生信号量(C20才有)&#xff0c;但可以通过 std::mutex std::condition_variable 模拟实现。以下是核心逻辑&#xff1a; #include <mutex> #include <condition_variable>class CountingSemaphore { private:…

web后端语言中篇

#作者&#xff1a;允砸儿 #日期&#xff1a;乙巳青蛇年 三月十八 笔者本来打算隔一天给它更完的&#xff0c;但是事情有点多这几天&#xff0c;实在是抱歉。废话不多说直接进入正题。 PHP流程控制语句 什么是流控:流程控制语句用于决定代码的执行顺序。 #注意流程控制语句…