C++ 标准模板库(STL)详解(容器、迭代器、算法、需要注意的概念)(逐步熟悉更新中···)

本文详细介绍了C++标准模板库(STL),包括容器、迭代器、算法及大小相等概念。容器分顺序和关联两类,迭代器是访问容器元素的中介,有不同类型和功能。STL提供通用算法,部分会改变容器,部分则不会。同时阐述了STL中大小和相等的比较规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++标准模板库(Standard Template Library,STL)

是一些常用数据结构(如链表、可变长数组、排序二叉树)和算法(如排序、查找)的模板的集合。

有了STL ,就不必编写大多数常用的数据结构和算法,且运行效率很高。


  1.  C++容器(STL容器)

容器(container)用于存放数据的类模板。

可变长数组、链表、平衡二叉树  等数据结构在STL中都被实现为容器。

当我们使用容器时,即将容器类模板实例化为容器类时,会指明容器中存放的元素是什么类型的。

容器中可以存放基本类型的变量,也可以存放对象。

(当对象或基本类型的变量被插入容器中时,实际插入的是对象或变量的一个复制品。)

容器分为两大类:

  • 顺序容器

顺序容器包括: 可变长动态数组 vector、双端队列 deque、双向链表 list

元素在容器中的位置与元素的值无关。将元素插入容器时,指定在什么位置插入,元素就会位于什么位置。

  1. 了解vector的使用方法可点击右方链接: C++ 容器 vector 详解及基本用法
  • 关联容器

关联容器包括:set、multiset、map、multimap

关联容器内的元素是排序的,插入元素时,容器会按一定的排序规则将元素放到适当的位置上,因此插入元素时不能指定位置

默认情况下,关联容器中的元素是从小到大排序的,且用 < 运算符比较元素或关键字大小。

因此关联容器在查找时具有非常好的性能。


容器 都是 类模板。实例化后就成为 容器类。用 容器类 定义的对象 称为 容器对象。

eg:  vector <int> 是一个容器类,vector <int> a;定义了一个容器对象a, a代表一个长度可变的数组,数组中的每个元素都是int类型的变量;vector<double> b;定义了另一个容器对象b,a和b的类型不同。

任何两个类型相同的容器对象,都可以用<、<=、>、>=、==、!=进行比较运算。

如a==b:当a和b中元素个数相同,且对应元素均相等,则a==b的值为true

a<b:从头到尾依次比较每个元素,若发生a中元素小于b中元素的情况,则值为true;若没有发生a中元素大于b中元素的情况,且a中元素个数比b少,则值也为true。


所有容器都有两个成员函数:

int size(): 返回容器对象中的元素个数。

bool empty(): 判断容器对象是否为空。

 

顺序容器与关联容器还包含以下函数:

begin():返回指向容器中的第一个元素的迭代器。

end():返回指向容器中的最后一个元素后面的位置的迭代器。

rbegin():返回指向容器中第一个元素前面的位置的反向迭代器。

rend():返回指向容器中的最后一个元素的反向迭代器。

clear():从容器中删除所有元素。

【如果一个容器是空的,则begin()和end()的返回值相等,rbegin()和rend()的返回值也相等。】

 

顺序容器还有以下常用成员函数:

front():返回容器中第一个元素的引用。

back():返回容器中最后一个元素的引用。

push_back():在容器末尾增加新元素。

pop_back():删除容器末尾元素。

insert(...):插入一个或多个元素。


  2.  C++迭代器(STL迭代器)iterator详解

要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行。

迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。

迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。(与指针类似)

迭代器按照定义分为以下四种。

  • 正向迭代器:
容器类名::iterator 迭代器名;
  • 容量正向迭代器:
容器类名::const_iterator 迭代器名;
  • 反向迭代器:
容器类名::reverse_iterator 迭代器名;
  • 常量反向迭代器:
容器类名::const_reverse_iterator 迭代器名;

迭代器用法示例

通过迭代器可以读取它指向的元素, *迭代器名 就表示迭代器指向的元素。通过非常量迭代器还能修改其指向的元素。

迭代器都可以进行 ++ 操作。

对正向迭代器进行 ++ 操作时,迭代器会指向容器中的后一个元素;

对反向迭代器进行 ++ 操作时,迭代器会指向容器中的前一个元素。

 

下面演示如何通过迭代器遍历一个vector容器中的所有元素。

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    vector<int> v;//v是存放int 类型变量的可变长数组,开始时没有元素

    for (int n=0;n<5;++n)
        v.push_back(n);//push_back成员函数:在vector容器尾部添加一个元素

    vector<int>::iterator i;//定义正向迭代器
    
    //用迭代器遍历容器
    for (i=v.begin();i!=v.end();++i){
        cout<<*i<<" ";//*i就是迭代器i指向的元素
        *i *=2;//每个元素变为原来的2倍
    }
    cout<<endl;
    
    //用反向迭代器遍历容器
    for(vector<int>::reverse_iterator j=v.rbegin();j!=v.rend();++j)
        cout<<*j<<" ";
    return 0;
}

程序的输出结果是:

0 1 2 3 4

8 6 4 2 0

迭代器功能分类

不同容器的迭代器,功能强弱有所不同。迭代器功能的强弱,决定了该容器是否支持STL中的某种算法。

例如:排序算法需要通过随机访问迭代器来访问容器中的元素。因此有的容器就不支持排序算法。

常用的迭代器按功能强弱分为 输入、输出、正向、双向、随机访问五种,这里只介绍常用的三种。

1)正向迭代器。

假设p是一个正向迭代器,则p支持以下操作:++p,p++,*p。此外,两个正向迭代器可以互相赋值,还可以用==,!=进行比较。

2)双向迭代器。

双向迭代器具有正向迭代器的全部功能,除此之外,若p是一个双向迭代器,则--p和p--都是有定义的,--p使得p朝着和++p相反的方向移动。

3)随机访问迭代器。

随机访问迭代器具有双向迭代器的全部功能。若p是一个随机访问迭代器,i 是一个整形变量或常量,则p还支持以下操作:

p+=i:使 p 向后移动 i 个元素;同理有p-=i

p+i:返回 p 后面第 i 个元素的迭代器;同理有p-i

p[i]:返回 p 后面第 i 个元素的引用。

此外,两个随机访问迭代器p1,p2还可以用运算符进行比较。

p1<p2的含义是:p1经过若干次(至少依次)++操作后,就会等于p2。

表达式p2-p1也有定义:其返回值是p2所指向的元素和p1所指向元素的序号之差,也就是p2和p1之间的元素个数-1.

不同容器的迭代器的功能
容器迭代器功能容器迭代器功能
vector随机访问map/multimap双向
deque随机访问stack不支持迭代器
list双向queue不支持迭代器
set/multiset双向priority_queue不支持迭代器

例如,list容器的迭代器是双向迭代器。假设v和i的定义如下:

list<int> v;
list<int>::const_iterator i;

 则以下代码合法:

for(i=v.begin();i!=v.end();++i)
    cout<<*i;

以下代码不合法:

for(i=v.begin();i<v.end();++i)
    cout<< *i;

因为双向迭代器不支持用运算符进行比较。以下代码也不合法:

for(int i=0;i<v.size();++i)
    cout<<v[i];

因为list不支持随机访问迭代器的容器,也就不支持用下标随机访问其元素。

【在C++中,数组也是容器。数组的迭代器就是指针,而且是随机访问迭代器。

例如,对于数组int a[10],int *类型的指针就是其迭代器。则a,a+1,a+2都是a的迭代器。

 

迭代器的辅助函数

STL中有用于操作迭代器的三个函数模板:

advance(p,n):使迭代器p向前或向后移动n个元素。

distance(p,q):计算两个迭代器之间的距离。即迭代器p经过多少次++操作后和迭代器q相等。

【若调用时p已经指向q后面,则函数会陷入死循环。】

iter_swap(p,q):用于交换两个迭代器p,q指向的值。

要使用以上模板,需要包含头文件 algorithm

#include<list>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int a[5] = { 1,2,3,4,5 };
	list <int> lst(a,a+5);
	list <int> ::iterator p = lst.begin();
	advance(p,2);//p向后移动两个元素,指向3
	cout<<"1)"<<*p<<endl;
	advance(p,-1);//p向前移动一个元素,指向2
	cout<<"2)"<<*p<<endl;
	list<int>::iterator q = lst.end();
	q--; //q指向5
	cout<<"3)"<<distance(p,q)<<endl;输出p和q的距离
	iter_swap(p,q);//交换2和5
	cout<<"4)";
	for(p=lst.begin();p!=lst.end();++p)
		cout<<*p<<" ";
	return 0;
}
程序运行结果

    3.STL算法

STL提供能在各种容器中通用的算法,如插入、删除、查找、排序等。算法就是函数模板,通过迭代器来操纵容器中的元素。

大部分算法的操作对象是容器上的一个区间(或整个容器),因此需要两个参数,一个是区间起点元素的迭代器,另一个是区间终点元素的后面一个元素的迭代器。例如,排序和查找算法都需要这两个参数来指明待排序或待查找的区间。

有的算法返回一个迭代器。例如,find算法在容器中查找一个元素,并返回一个指向该元素的迭代器。

算法可以处理容器,也可以处理普通的数组。

  • 有的算法会改变其所作用的容器,例如:

copy:将一个容器的内容复制到另一个容器。

remove:在容器中删除一个元素。

random_shuffle:随机打乱容器中的元素。

fill:用某个值填充容器。

  • 有的算法不会改变所作用的容器。例如:

find:在容器中查找元素。

count_if:统计容器中符合某种条件的元素的个数。

STL中的大部分常用算法都在头文件 algorithm 中定义。

下面介绍一个常用算法find,find模板的原型如下:

template <class InIt, class T>
InIt find(InIt first, InIt last, const T& val);

功能可以是 在迭代器 first 和 last 指定的容器的一个区间 [first,last)中,

按顺序查找和 val 相等的元素。如果找到,就返回该元素的迭代器,否则返回 last。

[first,last)是一个左闭右开的区间,即 last 指向的元素其实不在此区间内。

find用法:

#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
int main()
{
	int a[10]={10,20,30,40};
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::iterator p;
	p=find(v.begin(),v.end(),3);    //在v中查找3

	if(p!=v.end())                  //若找不到,find返回v.end()
		cout<<"1)"<<*p<<endl;   //找到,则返回该元素迭代器 

	p=find(v.begin(),v.end(),9);    //在v中查找9
	if(p==v.end())
		cout<<" not found "<<endl;//未找到

	p=find(v.begin()+1,v.end()-1,4);//在2,3这两个元素中查找4
	cout<<"2)"<<*p<<endl;

	int *pp = find(a,a+4,20);
	if(pp==a+4)
		cout<<" not found "<<endl;
	else
		cout<<"3)"<<*pp<<endl;
}
程序输出结果

首先,p=find(v.begin()+1,v.end()-1,4);这句中,要查找的区间是[v.begin()+1,v.end()-1),这个区间中只有2、3两个元素,

因此查找失败。p的值变为v.end()-1,因此,p正好指向4这个元素。

其次,int *pp = find(a,a+4,20);这行中,数组a是一个容器,数组名a 的类型是int *,可以做迭代器使用,本次调用find,查找区间是[a,a+4),即数组a的前4个元素,若查找失败,find就会返回a+4。

STL中还有一个常用的算法 sort,用于对容器排序,原型为:

template<class_RandIt)
void sort(_RandIt first,_RandIt last);

该算法可以用来对区间 [first,last) 从小到大进行排序。下面的程序就可以对数组a进行排序:

int a[4]={3,4,2,1};
sort(a,a+4);

     4.STL中“大”、“小”和“相等”的概念

STL中关联容器内部的元素是排序的,许多算法也涉及排序、查找。这些容器和算法都需要对元素进行比较。

在STL中,默认情况下,比较大小是通过 < 运算符进行的,和 > 运算符无关。

以下三个说法是等价的。

  • x 比 y 小
  • 表达式 x<y 为真
  • y 比 x 大

一定要注意:y比x大 意味着  x<y 为真,而不是 y>x为真 。y>x 的结果如何并不重要,甚至 y>x 是没定义的都没有关系。

在STL中, x和y相等 也往往不等价于 x==y为真。

(这个问题以后碰到具体情况理解深刻后再补充)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值