C++ STL & 标准库

2023-09-14 14:45:53

STL

         STL(标准模板库)是一套 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。

         C++ STL的核心包括三个组件:

容器(Containers)用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。
算法(Algorithms)算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。
迭代器(iterators)迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。

         三个组件都带有丰富的预定义函数,帮助我们通过简单的方式处理复杂的任务。

容器(vector )

定义

         Vector是最常用的容器之一,是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组,因为其大小是根据实时更新而变化的。

           vector的定义如下:

Vector<类型> 标识符
Vector<类型> 标识符(最大容量)
Vector<类型> 标识符(最大容量,初始所有值)
Vector<类型> v(i,i+2);     //得到i索引值为3以后的值
Vector< vector< int> >v;   //二维向量,这里最外的<>要有空格。否则在比较旧的编译器下无法通过

         例如:

vector<int> v;           // 储存int型的值 
vector<double> v;        // 储存double型的值 
vector<string> v;        // 储存string型的值 
vector<struct> v;        // 储存结构体或者类的值的值 

/* 可以定义vector数组 */
vector<int> a[n];        // 储存int型的值 
vector<double> a[n];     // 储存double型的值 

特性 

         容器特性:

  • 顺序序列:顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置访问对应的元素。
  • 动态数组:支持对序列中的任意元素进行快速直接访问,甚至可以通过指针算述进行该操作。提供了在序列末尾相对快速地添加/删除元素的操作。
  • 内存分配感知(Allocator-aware):容器使用一个内存分配器对象来动态地处理它的存储需求。

成员函数       

构造函数
vector();                       //创建一个空vector
vector(int nSize);              //创建一个vector,元素个数为nSize
vector(int nSize,const t& t);   //创建一个vector,元素个数为nSize,且值均为t
vector(const vector&);          //复制构造函数
vector(begin,end);              //复制[begin,end)区间内另一个数组的元素到vector中
增加函数
/* 向量尾部增加一个元素X */
void push_back(const T& x);    
/* 向量中迭代器指向元素前增加一个元素x */              
iterator insert(iterator it,const T& x); 
/* 向量中迭代器指向元素前增加n个相同的元素x */    
iterator insert(iterator it,int n,const T& x); 
/* 向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据 */
iterator insert(iterator it,const_iterator first,const_iterator last);
删除函数
iterator erase(iterator it);                   //删除向量中迭代器指向元素
iterator erase(iterator first,iterator last);  //删除向量中[first,last)中元素
void pop_back();                               //删除向量中最后一个元素
void clear();                                  //清空向量中所有元素

遍历函数
reference at(int pos);       //返回pos位置元素的引用
reference front();           //返回首元素的引用
reference back();            //返回尾元素的引用
iterator begin();            //返回向量头指针,指向第一个元素
iterator end();              //返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin();   //反向迭代器,指向最后一个元素
reverse_iterator rend();     //反向迭代器,指向第一个元素之前的位置
判断函数
bool empty() const;          //判断向量是否为空,若为空,则向量中无元素
大小函数
int size() const;             //返回向量中元素的个数
int capacity() const;         //返回当前向量所能容纳的最大元素值
int max_size() const;         //返回最大可允许的vector元素数量值

/* 如果n小于当前容器的大小,则将内容减少到其前n个元素,并删除超出范围的元素(并销毁它们)*/
void resize(size_type n);    

/**
  *如果n大于当前容器的大小,则通过在末尾插入所需数量的元素来扩展内容,以达到n的大小。
  *如果指定了val,则将新元素初始化为val的副本,否则将对它们进行值初始化。
  *如果n也大于当前容器容量,将自动重新分配已分配的存储空间 
  */
void resize(size_type n, const value_type& val);

/**
  *表示预分配n个元素的存储空间,但不是真正的创建对象,
  *需要通过insert()或push_back()等操作创建对象。
  *调用reserve(n)后,若容器的capacity<n,则重新分配内存空间,从而使得capacity等于n。
  *若capacity>=n,capacity无变化。
  */
void reserve(size_type n);
resize() 和 reverse()区别

         容器调用resize()函数后,所有的空间都已经初始化了,所以可以直接访问。而reserve()函数预分配出的空间没有被初始化,所以不可访问。区别:

  • reserve()只修改capacity大小,不修改size大小,
  • resize()既修改capacity大小,也修改size大小。

          下面用实例进行说明:

#include <iostream>
#include <vector>

using namespace std;

int main(void)
{
    vector<int> v;
    cout<<"v.size() == " << v.size() << " v.capacity() = " << v.capacity() << endl;
	
    v.reserve(10);
	cout<<v[5]<< endl;
    cout<<"v.size() == " << v.size() << " v.capacity() = " << v.capacity() << endl;
	
    v.resize(10);
    v.push_back(0);
    cout<<"v.size() == " << v.size() << " v.capacity() = " << v.capacity() << endl;

    return 0;
}

         执行结果如下:

v.size() == 0 v.capacity() = 0
1735357008
v.size() == 0 v.capacity() = 10
v.size() == 11 v.capacity() = 20

注: 对于:v.reserve(10) 后接着直接使用 [ ] 访问越界报错(内存是野的),这里直接用[ ]访问,vector 退化为数组,不会进行越界的判断。此时推荐使用 at(),会先进行越界检查,如下所示:

cout<<v.at(5)<< endl;

v.size() == 0 v.capacity() = 0
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 5) >= this->size() (which is 0)

相关引申:

         针对 capacity 这个属性,STL 中的其他容器,如 list、 map、set、deque,由于这些容器的内存是散列分布的,因此不会发生类似 realloc() 的调用情况,因此可以认为 capacity 属性针对这些容器是没有意义的,因此设计时这些容器没有该属性。在 STL 中,拥有 capacity 属性的容器只有 vector 和 string。

capacity() 和 size() 的区别
  • size() :当前 vector 容器真实占用的大小,也就是容器当前存储的个数。
  • capacity() :容器可存储的最大总数,即预分配的内存空间。

         这两个属性分别对应两个方法:resize() 和 reserve()。       

其他
/* 交换两个同类型向量的数据 */
void swap(vector&);
/* 设置向量中前n个元素的值为x */
void assign(int n,const T& x);//
/* 向量中[first,last)中元素设置成当前向量元素 */
void assign(const_iterator first,const_iterator last);

实例

pop_back() & push_back()

         在容器最后移除和插入数据:

#include<iostream>
#include<vector>
#include<string.h>

using namespace std;

int main()
{
	vector<int> obj;               // 创建一个向量存储容器 int
	for(int i=0;i<10;i++)          // 依次在数组最后添加数据
	{
		obj.push_back(i);
		cout<<obj[i]<<"\t";
	}
	
	for(int j=0;j<5;j++)           // 依次去掉数组最后一个数据 
	{
		obj.pop_back();
	}
	
	cout<<"\n"<<endl;
	
	for(int k=0;k<obj.size();k++)  // size()返回容器中实际数据个数 
	{
		cout<<obj[k]<<"\t";
	}
	
	return 0;
}

         执行结果如下:

0       1       2       3       4       5       6       7       8       9

0       1       2       3       4

clear()

         清除容器中所有数据:

#include<string.h>
#include<vector>
#include<iostream>

using namespace std;

int main()
{
	vector<int> obj;
	for(int i=0;i<10;i++)
	{
		obj.push_back(i);
		cout<<obj[i]<<"\t";
	}
	
	obj.clear();
	cout<<endl;
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	cout<<endl<<"size = "<<obj.size()<<", capacity = "<<obj.capacity();
	
	return 0;
}

         执行结果如下:

0       1       2       3       4       5       6       7       8       9

size = 0, capacity = 16

排序

         sort() 函数需要包含头文件: #include <algorithm>

#include<string.h>
#include<vector>
#include<iostream>
#include<algorithm>    // 'sort()' and 'reverse()' was declared in this Header file

using namespace std;

int main()
{
	vector<int> obj;
	
	obj.push_back(1);
	obj.push_back(2);
	obj.push_back(3);
	
	cout<<"由小到大:"<<endl;
	sort(obj.begin(),obj.end());
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	
	cout<< endl <<"由大到小:"<<endl;
	reverse(obj.begin(),obj.end());
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	
    return 0;	
}

         执行结果如下:

由小到大:
1       2       3

由大到小:
3       2       1

         如果想 sort 来降序,可重写 sort

/* 升序排列,如果改为return a>b,则为降序  */
bool compare(int a,int b) 
{ 
    return a< b; 
} 

/* 示例 */
int a[20]={2,4,1,23,5,76,0,43,24,65},i; 
for(i=0;i<20;i++) 
{
    cout<< a[i]<< endl; 
}
sort(a,a+20,compare);

         如下所示:

#include<string.h>
#include<vector>
#include<iostream>
#include<algorithm>    // 'sort' and 'reverse' was declared in this Header file

using namespace std;

bool compare(int a,int b) 
{ 
    return a > b; 
} 

int main()
{
	vector<int> obj;
	
	obj.push_back(1);
	obj.push_back(2);
	obj.push_back(3);
	
	cout<<"由小到大:"<<endl;
	sort(obj.begin(),obj.end());
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	
	cout<< endl <<"由大到小:"<<endl;
	sort(obj.begin(),obj.end(),compare);  //sort 来降序
	for(int i=0;i<obj.size();i++)
	{
		cout<<obj[i]<<"\t";
	}
	
    return 0;	
}
直接数组访问&迭代器访问
#include<iostream>
#include<string.h>
#include<vector>
#include<algorithm>

using namespace std;

int main()
{
	vector<int> obj;
	for(int i=0;i<10;i++)
	{
		obj.push_back(i);
	}

	// 方法一:直接数组访问
	cout<<"直接利用数组:"<<"\t";
	for(int i=0;i<10;i++)
	{
		cout<<obj[i]<<"\t";
	}
	
	// 方法二:迭代器访问
	cout<<endl;
	cout<<"利用迭代器:"<<"\t";
	vector<int>::iterator it;
	for(it=obj.begin();it!=obj.end();it++)
	{
		cout<<*it<<"\t";
	}
	
	return 0;
}

         执行结果如下:

直接利用函数:  0       1       2       3       4       5       6       7       8       9
利用迭代器:     0       1       2       3       4       5       6       7       8       9

二维数组两种定义方法

         假设定义一个二维数组obj[5][6],两种定义方法的结果一样:

方法一
#include<iostream>
#include<vector>
#include<string.h>
#include<algorithm>

using namespace std;

int main()
{
	int N=5,M=6;                   // 定义二维动态数组大小5行为5行6列,值默认全为0
	vector< vector<int> > obj(N);  // 开辟行
	for(int i=0;i<obj.size();i++)
	{
		obj[i].resize(M);          // 开辟行
	}
	
	for(int i=0;i<obj.size();i++)  // 赋值,并输出二维动态数组 
	{
		for(int j=0;j<obj[i].size();j++)
		{
			obj[i][j]=i+j;
			cout<<obj[i][j]<<"\t"; 
		}
		cout<<endl;
	}
	
	return 0;
}
方法二
#include<iostream>
#include<vector>
#include<string.h>
#include<algorithm>

using namespace std;

int main()
{
	int N=5,M=6;                   // 定义二维动态数组大小5行为5行6列,值默认全为0
	
	/* 创建一个vector,元素个数为N,且值均为一个元素个数为M的vector<int>(M) */ 
	vector< vector<int> > obj(N,vector<int>(M)); 
	
	for(int i=0;i<obj.size();i++)  // 赋值,并输出二维动态数组 
	{
		for(int j=0;j<obj[i].size();j++)
		{
			obj[i][j]=i+j;
			cout<<obj[i][j]<<"\t"; 			
		}
		cout<<endl;
	}

	return 0;
}

         输出结果为:

0       1       2       3       4       5
1       2       3       4       5       6
2       3       4       5       6       7
3       4       5       6       7       8
4       5       6       7       8       9

标准库

         C++ 标准库可以分为两部分:

  • 标准函数库: 由通用的、独立的、不属于任何类的函数组成的。它继承自 C 语言。
  • 面向对象类库: 这个库是类及其相关函数的集合。

         C++ 标准库包含了所有的 C 标准库,为了支持类型安全,做了一定的添加和修改。

标准函数库

         标准函数库分为以下几类:

  • 输入/输出 I/O
  • 字符串和字符处理
  • 数学
  • 时间、日期和本地化
  • 动态分配
  • 其他
  • 宽字符函数

面向对象类库

         面向对象类库定义了大量支持一些常见操作的类,比如输入/输出 I/O、字符串处理、数值处理。面向对象类库包含以下内容:

  • 标准的 C++ I/O 类
  • String 类
  • 数值类
  • STL 容器类
  • STL 算法
  • STL 函数对象
  • STL 迭代器
  • STL 分配器
  • 本地化库
  • 异常处理类
  • 杂项支持库

更多推荐

深入探究序列化与反序列化:原理、应用和最佳实践

目录什么是对象的序列化和反序列化序列化步骤反序列化步骤案例演示Java中哪些字段不能序列化序列化与反序列化的重要性序列化与反序列化的应用场景什么是对象的序列化和反序列化序列化(Serialization)是指将对象转化为字节流的过程,以便于存储或通过网络进行传输。反序列化(Deserialization)则是将字节流转

检测特定IP端口是否可达

目录背景方法方法一:使用nmap方法二:使用telnet背景日常工作中经常需要判定某个IP的端口是否可达,之前一直使用telnet工具,但今天遇到在某特定的设备上没有该工具(软件源里也没有,无法安装)的问题,于是以此契机稍微研究了下其他的检测方式,整理在此。方法方法一:使用nmap首先确保计算机已安装nmap。然后在终

Java POI 读取 大数据量的Excel 实操

JavaPOI读取大数据量(超过10W行)的excel的操作0.问题抛出在使用poi进行excel文件读取操作的时候,如果文件包含的数据量很大,比如包含了10万行的数据,那么在使用【Workbookworkbook2=WorkbookFactory.create(inputStrem);】这种形式读取的时候就会发现异常

线性代数的本质(五)——矩阵的运算

文章目录矩阵的运算矩阵的转置方阵的运算初等矩阵分块矩阵逆矩阵矩阵的秩广义逆矩阵矩阵的运算矩阵的转置转置:矩阵AAA的行列互换得到的矩阵称为AAA的转置(transpose),记作ATA^TAT。性质:矩阵转置运算满足下列性质:(A+B)T=AT+BT(A+B)^T=A^T+B^T(A+B)T=AT+BT(AT)T=A(

杂记 | Langchain中few-shot提示词模板的使用(给提示词添加示例)

文章目录01普通的提示词模板02few-shot提示词模板Langchain是一个集成多个大语言模型的开源框架,可以使用它来快速开发大语言模型应用。本文的代码使用到的模块:fromtypingimportList,DictfromlangchainimportPromptTemplate,FewShotPromptTe

Itsycal for Mac: 精美日历软件的魅力之旅

在这个数字化时代,管理时间和日程变得尤为重要。macOS平台上的Itsycal日历软件可以帮助你有效管理你的日程和时间。Itsycal是一款轻量级且直观的日历应用程序,专门为macOS用户设计。通过这款软件,你可以轻松查看、管理和跟踪你的日常活动和重要日期。下面是一些Itsycal为macOS用户带来的独特功能:简洁直

第十章 数据库恢复技术

第十章数据库恢复技术10.1事务的基本概念事务事务是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。例事务的特性(ACID特性(ACIDproperties))原子性(Atomicity)事务是数据库的逻辑工作单位,事务中包括的诸操作要么都做,要么都不做。一致性(Consisten

微软在Windows 11推出Copilot,将DALL-E 3集成在Bing!

美东时间9月21日,微软在美国纽约曼哈顿举办产品发布会,生成式AI成为重要主题之一。微软表示,Copilot将于9月26日在Windows11中推出;Microsoft365Copilot将于11月1日向企业客户全面推出;将OpenAI最新的文本生成图片产品DALL.E3集成在Bing和设计平台Designer中等。简

通过实现HandlerInterceptor接口实现一个拦截器

1.简介web应用开发中,拦截器的应用场景非常广泛,主要用于:登陆验证:提取request中请求头携带的token信息;鉴权:判断该用户是否有权限访问某个资源日志记录:记录该handler的入和出性能监控、通用行为等等一些其它的操作。2.spring中使用拦截器的方式spring为我们提供了一个接口:HandlerIn

Python中转换IP地址格式的方法

IP地址一般用字符串“XXX.XXX.XXX.XXX”表示。例如,“192.168.147.1”、“127.0.0.1”等。在确定主机IP地址段时,需要将IP地址的每段转换成数字。1inet_aton()方法该方法的使用方法是socket.inet_aton(ip_string)其中,参数ip_string是字符串类型

GB28181协议-SDP详解

SDP协议SDP全称是SessionDescriptionProtocol,翻译过来就是描述会话的协议。主要用于两个会话实体之间的媒体协商。SDP描述由许多文本行组成,文本行的格式为<类型>=<值>,表示为key=value;SIP负责建立和释放会话,一般来说,会话中包含相关的媒体,比如视频和音频。媒体数据是由SDP描

热文推荐