【C++】STL标准模板库

news2024/11/23 19:29:04

目录

一、概念

STL的四种基本组件

容器vector

迭代器iterator

函数对象function object

算法algorithm

二、使用

容器vector的使用


泛型程序设计:
        所谓泛型程序设计就是编写不依赖于具体数据类型的程序。C++中,模板就是泛型程序设计的主要工具。

        泛型程序设计的主要思想是将算法从特定的数据结构中抽象出来,使算法成为通用的、可以作用于各种不同的数据结构。这样就不必为每种容器都编写一套同样的算法,当容器类模板修改、扩充时也不必重写大量算法函数。这种以模板函数形式实现的通用算法和各种通用容器结合,提高软件复用性。

一、概念

  •         标准模板库STL是一款用于支持C++泛型程序的模板库。由于C++标准库有多种不同的实现,因此STL也有不同的版本,但是他们为用户提供的接口都遵守相同的标准。
  •         STL提供了一些常用的数据结构和算法。他更远大的意义是,它定义了一套概念体系,为泛型程序设计提供了逻辑基础。

STL的四种基本组件

容器vector

容器是容纳、包含一组元素的对象。容器类库中包含7种基本容器:

  1. 向量vector
  2. 双端队列deque
  3. 列表list
  4. 集合set
  5. 多重集合multiset
  6. 映射map
  7. 多重映射multimap

 

 STL中各容器头文件和所属概念

这7种容器可以分为两种基本类型:

  1. 顺序容器aequence container:将一组具有相同类型的元素以严格的线性形式组织起来,向量、双端队列和列表容器就属于这一种。
  2. 关联容器associative container:具有根据一组索引来快速提取元素的能力,集合和映射容器就属于这一种。

迭代器iterator

  • 迭代器提供了顺序访问容器中每个元素的方法。
  • 对于迭代器可以使用“++”运算符来获得指向下一个元素的迭代器,可以使用“*”运算符来访问一个迭代器所指向的元素
  • 如果元素类型是类或者结构体,还可以使用“->”运算符直接访问该元素的一个成员,有些迭代器还支持通过“--”运算符获得指向上一个元素的迭代器。
  • 指针也具有同样的特性,因此指针本身也是一种迭代器,迭代器是泛化的指针,但是迭代器不仅仅是指针。指针可以指向内存中的一个地址,通过这个地址就可以访问相应的内存单元;而迭代器更为抽象,它可以指向容器中的一个位置,我们不关心这个位置对应的真正物理地址,只需要通过迭代器访问这个位置的元素。

函数对象function object

  • 函数对象是一个行为类似函数的对象,对它可以像调用函数一样调用。任何普通的函数和任何重载了“()”运算符的类的对象都可以作为函数对象使用,函数对象是泛化的函数。

算法algorithm

  • STL包含70多个算法,这些算法覆盖了相当大的应用领域,其中包含查找算法、排序算法、消除算法、计数算法、比较算法、变换算法、置换算法和容器管理等。
  • 这些算法的一个最重要的特性就是它们的统一性,并且可以广泛用于不同的对象和内置的数据类型。
  • 标准模板库 = 容器(类模板)+算法(函数模板)
  • 标准模板库的特点是不依赖于类型存在,是个模板。
  • 容器:vector queue stack list set map multiset multimap... 容器是用来存储数据
  • 算法:copy sort find find_if count count_if swap 算法用来操作容器内的元素
  • 迭代器是容器和算法的粘合剂,相当于一个指针,将容器内的元素访问出来,通过算法去操作。容器是封装起来的类模板,其内部结构无人知晓,只能通过容器接口去使用容器。但是STL中的算法是通用的函数模板,而不是专门针对某个容器类型。算法要适用于多种容器,而每一种容器中存放的元素又可以是任何类型,这就必须使用更为抽象的指针——迭代器
  • 仿函数:称为函数对象,将其放在算法内,可以将算法的功能得以扩展

 STL组件之间的关系

二、使用

容器vector的使用

用vector中定义对象及初始化

#include<iostream>
#include<vector>
#include<iterator>
#include<algorithm>

int main()
{
	//定义对象及初始化的使用:
	vector<int> vv1;//定义容器vv1
	vector<int> vv2(5);//定义大小为5的容器vv2,初始化为5个0,只写一个值默认为个数,值为0
	vector<int> vv3(4, 9);//定义大小为5的容器vv3,初始化为4个9

	int num[5] = { 1,2,3,4,5 };
	vector<int> vv4(num, num + 5);//前闭后开区间将vv4初始化为1 2 3 4 5,将区间元素插入到vv4内
	vector<int> vv5(vv4.begin(), vv4.end());//vv4的begin到vv4的end初始化vv5
	vector<int> vv6(vv3);//通过vv3拷贝构造得到vv6,vv6复制的结果为4个9
	vector<int> vv7;
	vv7 = vv2;//vv7被初始化为vv2,也就是0
	vv4[1] = 10;//将vv4[1]赋值为10,对象+中括号可以使用,说明重载了中括号★
	//vv1[1] = 10;但这样错误,因为vv1[1]大小没赋值,但是可以用push_back压数据
	vv1.push_back(1);//vv1=1;
   
     //从标准输入读入向量容器的内容
	for (int i = 0; i < 5; i++)
		cin >> vv2[i];
}

其中,vv4是个成员函数,如下,使用“.”就自动出来其成员函数

对象+中括号可以使用,说明重载了中括号

容器基本功能函数

设S表示一种容器类型,s1和s2是S类型的实例,容器支持的基本功能如下:

  • S s1 容器都有一个默认构造函数,用于构造一个没有任何元素的的空容器
  • s1 op s2 这里的op可以是==,!=,<,<=,>,>=之一,他会对两个容器之间的元素按字典顺序比较
  • s1.beigin():返回指向s1的第一个元素的地址
    • cbegin:const类型
    • crbegin:逆序
  • s1.end():返回指向s1最后一个元素的下一个的地址
vector<int>::iterator iter;
for(iter=vv1.begin();iter!=vv1.end();iter++)
{
    cout<<*iter<<" ";
}
  • s1.clear():将容器s1的内容清空
  • s1.empty():返回一个布尔值,表示s1容器是否为空
  • s1.size():返回s1的元素个数
  • s1.swap(s2):将s1容器和s2容器内容交换

(1)构造函数

        顺序容器除了具有默认构造函数外,还可以使用给定的元素构造,也可以使用已有的迭代器的区间所表示的序列来构造:

S s(n,t);//构造一个由n个t元素构成的容器实例s
S s(n); //构造一个有n个元素的容器实例s,每个元素都是T()
S s(q1,q2); //使用将[q1,q2)区间内的数据作为s的元素构造s

(2)赋值函数

        可以使用下面成员函数assign将指定的元素赋给顺序容器,顺序容器中原先的元素会被清除,赋值函数的3种形式是与构造函数一一对应的。

s.assign(n,t);//赋值后的容器由n个t元素构成
s.assign(n); //赋值后的容器有n个元素的容器实例s,每个元素都是T()
s.assign(q1,q2); //赋值后的容器的元素为[q1,q2)区间内的数据

 assign举例:

//区间赋值
vv1.assign(5,6);//将vv1重新赋值为5个6

int num={1,2,3,4,5};
vv1.assign(num,num+5);//将vv1重新赋值为1 2 3 4 5 

vv1.assign(vv4.begin(),vv4.begin()+3);//将vv1重新赋值为vv2的第一个和第4个之间的数值

copy也是复制:

copy(vv4.begin(),vv4.end(),ostream_iterator<int>(cout," ”); 
//vv4.begin到ee4.end区间内的值用输出迭代器输出
//第二个参数可以不写," "用空格隔开
//ostream_ierator<int>是个类模板

不仅可以操作容器,也可以对数组操作:

int num[]={1,2,3,4,5};
int numdes[5];
copy(num,num+5,numdes);//把num到num+5拷贝到numdes内

 (3)元素的插入

        向顺序容器中可以一次插入一个或多个指定元素,也可以将一个迭代器区间所表示的序列插入。插入时需要通过一个指向当前容器元素的迭代器来指示插入位置,如下:

s.insert(p1, t);//在s容器中p1所指向的位置插入一个新的元素t
//插入后的元素夹在原pl和p1-1所指向的元素之间,该函数会返回一个迭代器指向新插人的元素。
s.insert(pl, n, t);//在s容器中pl所指向的位置插人n个新的元素t,
//插入后的元素夹在原pl和p1-1所指向的元素之间,没有返回值。
s.insert(pl, q1, q2);//将[q1, q2)区间内的元素顺序插入到s容器中p1位置处,
//新元素夹在原p1和p1-1所指向的元素之间。

 insert

vv.insert(vv4.begin(),5,10);//vv4.begin()位置插入5个10,原有数据后移
vv.insert(vv4.begin()+4,num,num+5);
//vv4.begin()+4位置插入num-num+5这个区间的值,原有数据后移

(4)元素的删除

        使用下面的函数可以从容器中删除指定元素或清空容器。删除指定元素时需要通过指向当前容器元素的迭代器来指示被删除元素的位置或区间。

sl. erase(pl);
//删除s1容器中p1所指向的元素,返回被删除的下一个元素的迭代器。
s1. erase(pl, p2);
//删除sl容器中[pl, p2)区间内的元素,
//返回最后一个被删除元素的下一个元素的迭代器(即在删除前p2所指向元素的迭代器)。

 erase

vv.erase(vv.begin()+2,vv.begin()+4);//区间内删掉

(5)改变容器的大小

可以通过下面的函数改变容器的大小。

s1.resize(n);
//将容器的大小变为n,如果原有的元素个数大于n,则容器末尾多余的元素会被删除;
//如果原有的元素个数小于n,则在容器末尾会用T()填充。

(6)首位元素的直接范围

可以通过顺序容器的成员函数快捷地访问容器的首尾元素,如下:

  • front:第一个元素
  • back:最后一个元素
s. front();
//获得容器首元素的引用。
s. back();
//获得容器尾元素的引用。

(7)在容器尾部插入、删除元素
虽然说使用insert成员函数可以在任意位置插入元素,使用erase成员函数可以删除任意位置的元素,但由于在顺序容器尾部插入、删除元素的操作更为常用,因此STL提供了更加便捷的成员函数,如下:

s.push_back(t);//向容器尾部插入元素t。
s.pop_back();//将容器尾部的元素删除。

  push_back:最后添加一个元素(参数为要插入的值)

vv.push_back(vv4.begin(),5,10);//vv4.begin()位置插入5个10,原有数据后移

(8)在容器头部插人、删除元素
列表list和双端队列deque两个容器支持高效地在容器头部插人新的元素或删除容器头部的元索,但是向量容器vector不支持。支持这一操作的概念构成了“前插顺序容器”(FrontInsertionSequence)这一概念,它是“顺序容器”的子概念。这些操作包括以下两种。

s.push_front(t);//向容 器头部插人元素t
s.pop_front();//删除容器头部的元素t.

(9)at:

用法:取下标,相当于'[]'

vv1.at(1) ; //输出vv1[1]的值,可作为左值返回就是引用

vv1.at(2)=50;  //将vv1[1]修改为50

(10)emplace

把当前位置所指向的值改成value

vv4.emplace(vv4.begin(),100);
vv4.emplace(200);//最后插入一个200

迭代器的使用

    //对容器进行访问
	vector<int>::iterator iter; //定义迭代器iter
    //iter++就是指向下一个元素
	//其中每个容器封装了自己的迭代器,因此声明迭代器类型和指向类型一致
    //每个容器封装自己的迭代器,vector的是vector<>,list是list<>
	/*拿指针来理解迭代器
	*int *p=num; 此时int与vector<int>类似;*与iterator类似
	*iterator就说明iter是迭代器类型,vector<int> 是迭代器指向的基类型
	*/

 输入流迭代器

template<class T>istream_iterator<T>;

其中,T是使用该迭代器从输入流到输入数据的类型。类型T要满足两个条件:有默认构造函数;对该类型的数据可以使用“>>”从输入流输入。一个输入流迭代器的实例需要由下面的构造函数来构造:

istream_iterator(istream& in);

在该构造函数中,需要提供用来输入数据的输入流(例如cin)作为参数。

  • 一个输入流迭代器实例支持“*”,“->”,“++”等几种运算符。
  • 用“*”可以访问刚刚读取的元素
  • 用“++”可以从输入流中读取下一个元素
  • 若类型T是类类型或者结构类型,用“->”可以直接访问刚刚读取元素的成员。

输出流迭代器

输出流迭代器用来向一个输出流中连续输出某种类型的数据,它也是一个类模板:

template<class T>ostream_iterator<T>;

        其中T表示向输出流中输出数据的类型,类型T需要具有一个功能:对该类型的数据可以使用"<<"向输出流输出。

        一个输出流迭代器可以用下面两个构造函数来构造:

ostream_iterator(ostream& out);
ostream_iterator(ostream& out,const char* delimiter);

        构造函数的参数out表示将数据输出到的输出流。参数delimiter是可选的,表示两个输出数据之间的间隔符。输出流得带起也支持"*"运算符,但对于一个输出迭代器iter,*iter只能作为赋值运算符的左值。例如*iter=x,这相当于执行了out<<x或out<<x<<delimiter

        输出流迭代器也支持“++”运算符,但该运算符实际上并不会使该迭代器的状态发生任何改变,支持“++”运算仅仅是为了让它和其他迭代器有统一的接口。

        在两种迭代器的帮助下,输入流和输出流可以直接参与STL算法,这就是引入两种迭代器的意义。输入流迭代器和输出流迭代器被看做一种适配器。适配器是指用于已有对象提供新的接口的对象,适配器本身一般并不提供新的功能,只为了改变对象的接口而存在。


 对输出方式总结:

1.迭代器iter遍历访问输出

vector<int>::iterator iter;
for(iter=vv1.begin();iter!=vv1.end();iter++)
    cout<<*iter<<" ";
cout<<endl;

2.输出迭代器ostream_iterator<>()

3.for_each()

void print(int n)
{
        cout<<n<<endl;
}
for_each(vv4.begin(),vv4.end(),print);
//print是自己写的,想求和也可以自定义sum函数等

class Print
{
public:
    void operator()(int n)//重载operator()
    {
        cout<<n<<" ";
    }
};
for_each(vv4.begin(),vv4.end(),Print());//函数对象

template<class T>
class Print
{
public:
    void operator()(T n)//重载operator()
    {
        cout<<n<<" ";
    }
};
for_each(vv4.begin(),vv4.end(),Print<int>());//模板


 用实例对容器和迭代器进行简单的使用

class Student
{
public:
    Student(int num=0,string name=""):m_num(num),m_name(name){}
    int GetNum()const//常成员函数后面加const表示不能修改本类数据成员的值
        return m_num;
    String GetName()const
        return m_name;
private:
    int m_num;
    string m_name;
}
bool cmp(Student& a,Student& b)//vs.begin()是指针,需要"&"
{
    return a.GetNum()<b.GetNum();
}
void main()
{
    vector<Student>vs;
    vs.push_back(Student(1001,"aaa"));
    vs.push_back(Student(1002,"bbb"));
    vector<Student>::iterator iter;
    //访问输出
    for(iter=vs.begin();iter!=vs.end();iter++)
    {
        //(*iter).m_num不对,因为是私有数据成员,因此添加接口GET
        cout<<iter->GetNum()<<" "<<iter->GetName()<<endl;//此时可以(*iter).
        //"."是对象使用,"->"是指针使用
    }
    //排序
    sort(vs.begin(),vs.end(),cmp);
}    

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

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

相关文章

一次疑似 JVM native 内存泄漏的排查实录

最近开发同学反馈&#xff0c;某定时任务服务疑似有内存泄漏&#xff0c;整个进程的内存占用比 Xmx 内存大不少&#xff0c;而且看起来是缓慢上升的&#xff0c;做了下面这次分析&#xff0c;包括下面的内容&#xff1a; 分析 JVM native 内存的一些常见思路内存增长了&#x…

关于Arduino连接L298N供电问题

关于Arduino连接L298N供电问题 查看原文 该L298N板声称有一个5V稳压器为Arduino供电&#xff0c;在这种情况下&#xff0c;您可以使用单个电源&#xff0c;并让电机板为Arduino供电。 关于为Arduino和电机提供动力有两种思想流派&#xff1a; 使用两个独立的电源&#xff0…

NumPy 的使用

NumPy&#xff08;Numerical Python&#xff09;是Python 语言的一个扩展程序库&#xff0c;支持大量的维度数组与矩阵运算&#xff0c;同时也针对数组运算提供大量的数学函数库。 NumPy 的前身 Numeric 最早由 Jim Hugunin 与其他协作者共同开发&#xff0c;2005 年&#xff0…

百万千万爆款视频的脚本是怎么写出来的?两套模板教你做同款

那些百万千万爆款视频的脚本是怎么写出来的&#xff1f;两套模板教你做同款。 每天都能刷到百万赞的短视频&#xff0c;看看自己的视频点赞量&#xff0c;失落是一种感觉&#xff0c;其实你也可以做出优秀的爆款文案。 今天给大家介绍两种短视频脚本模板&#xff0c;大家可以…

idea手动创建干净的maven项目,很简单

大家好&#xff0c;今天我们分享使用idea开发工具创建干净的maven项目 这是Maven的官网&#xff1a; 点一下就可以 首先&#xff0c;我们来了解一下什么是Maven&#xff0c;就是说关于Maven这个东西你要知道的是 1.Maven是一个跨平台&#xff08;在很多平台上都可以使用&…

B4:Unity制作Moba类游戏——小兵AI系统

若想取得战争的胜利&#xff0c;必先控好兵线。 ———— 麦克阿瑟 是时候让敌人经历一下我们兵线的洗礼。 ———— 拿破仑 在LOL对局中&#xff0c;职业选手对兵线的控制可以说是达到了“运筹帷幄之中,决胜千里之外”。其实普通玩家只要控好兵线&#xff0c;在对线中一样可以…

Java Servlet详解(补充,极其重要)

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;JAVA开发者…

SwiftUI 中列表行(List Row)展开和收起无动画或动画诡异的解决

文章目录 问题现象问题分析1. 为什么 List 行展开与收起没有动画效果?2. 第一种解决方法3. 另一种巧妙的解决总结结束语问题现象 SwiftUI 中展开(expand)和收起(collapse)列表行(List Row)是一个常见的操作,不过默认来说这样的操作不会有动画效果: 如上图所示,我们为…

粒子滤波算法(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

CpG ODN——艾美捷ODN 1826 (TLRGRADE)说明书

艾美捷CpG ODN系列——ODN 1826 (TLRGRADE)&#xff1a;具有硫代磷酸酯骨架的CpG寡脱氧核苷酸&#xff08;B型&#xff09;。小鼠TLR9&#xff08;Toll样受体9&#xff09;的特异性配体。 艾美捷CpG ODN 丨ODN 1826 (TLRGRADE)化学性质&#xff1a; 备选名称&#xff1a;CpG-B…

Suspense组件

先上官网&#xff1a;https://cn.vuejs.org/guide/built-ins/suspense.html 注意一下 <Suspense> 是一项实验性功能。它不一定会最终成为稳定功能&#xff0c;并且在稳定之前相关 API 也可能会发生变化。 在使用了之后在浏览器控制台会有如下打印&#xff0c;至少目前是…

【大数据】有关zookeeper的问题

如图&#xff0c;启动zookeeper失败&#xff0c;输入 zkServer.sh start-foreground 查看失败原因 Invalid config&#xff0c;我得知是配置文件出了问题&#xff0c;但是检查配置文件没有发现错误 最终在配置文件末尾配置参数结尾发现了未删除的空格 将三个节点配置文件中的…

C/C++ 和 Java的编译运行机制比较 个人理解

计算机程序语言按程序的执行方式可分为编译型语言和解释性语言。 编译型语言是指使用专用的编译器&#xff0c;针对某操作系统将高级语言源代码一次性地翻译成可被该系统硬件执行的机器码(包括机器指令和操作数&#xff09;&#xff0c;并包装成该系统所能识别的可执行程序的格…

同事开源我的微服务深度实践笔记到 GitHub,短短 3 天竟吸粉 1W+

说Spring成就了Java&#xff0c;Spring是Java程序员必修课之一&#xff0c;应该没人反对吧&#xff1f;前几年面试最常问的且可以顺利拿到高薪的技能是Spring&#xff0c;随着Spring体系的壮大&#xff0c;除非你在简历上添加Spring Boot和Spring Cloud的技能&#xff0c;才可以…

Kubernetes——Debug Static Pod

1. 问题背景 注意&#xff0c;我这里的Static Pod并非Kubernetes的Static Pod&#xff0c;而是需要把想要Debug的程序放到Delve环境中重新打包一个镜像。因为还有另外一种场景&#xff0c;那就是我们需要不重启Running Pod&#xff0c;为了和这种方式区分&#xff0c;才以此为…

彻底卸载并重装Anaconda环境与Python的方法

本文介绍在Windows平台下&#xff0c;彻底删除Anaconda环境与其自带Python版本&#xff0c;并进行重新安装的方法。 最近&#xff0c;由于原有Anaconda环境中的部分第三方库出现了冲突的情况&#xff0c;且基于“Anaconda Prompt (anaconda3)”也无法升级Anaconda与相关库了&am…

《超新星纪元》

《超新星纪元》 关于作者 刘慈欣&#xff0c;髙级工程师&#xff0c;科普作家&#xff0c;被誉 为"中国当代科幻第一人"。自上世纪90年代开始&#xff0c;他一边在发电厂担任计算机工程师&#xff0c;一边利用业余时间出版了13本小说集&#xff0c;连续数年获得中国…

这是一篇讲解用户行为分析的推荐书单和总结

写在前面 技术文延迟了 本来计划参加活动的还有一篇&#xff0c;应该是一篇技术翻译文&#xff0c;但是那篇文章太难了&#xff0c;看我过我以往文章的同学&#xff0c;应该能理解&#xff0c;我的文章很少有3000字数以下的&#xff0c;而且如果不是来自谷歌&#xff08;主要…

深蓝学院-多传感器融合定位课程-第9章-基于图优化的建图方法

专栏文章: 深蓝学院-多传感器融合定位课程-第1章-概述_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第2章-3D激光里程计I_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第3章-3D激光里程计II_goldqiu的博客-CSDN博客 深蓝学院-多传感器融合定位课程-第…

2022品牌出海:日本网红营销白皮书

日本作为世界第三大经济体&#xff0c;同时也是世界第四大电子商务市场&#xff0c;亚洲第二大消费市场&#xff0c;其经济水平和消费能力都非常出色。对出海企业来说&#xff0c;日本是一个非常有吸引力的市场。日本的网红营销市场也非常成熟&#xff0c;在疫情的影响下&#…