C++ STL标准模板库简介

本文深入介绍了C++标准模板库(STL)的核心概念和技术细节,包括STL的组成、优势及不同版本的区别。详细探讨了六大组件的特点,并通过实例讲解了迭代器的设计与应用。

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


C++ STL标准库系列文章:

[STL] 1.简介
[STL] 2.序列容器 固定数组array(C++ 11)
[STL] 3.序列容器 动态数组vector
[STL] 4.序列容器 双端队列deque
[STL] 5.序列容器 双向链表list
[STL] 6.序列容器 单向链表forward_list(C++ 11)
[STL] 7.适配器简介
[STL] 8.容器适配器 栈stack
[STL] 9.容器适配器 队列queue
[STL] 10.容器适配器 优先队列priority_queue
[STL] 11.关联容器 集合set
[STL] 12.关联容器 映射map
[STL] 13.关联容器 多重集合multiset
[STL] 14.关联容器 多重映射multimap
[STL] 15.关联容器 无序集合unordered_set(C++ 11)
[STL] 16.关联容器 无序集合unordered_map(C++ 11)
[STL] 17.仿函数functor与函数对象
[STL] 18.预定义函数对象、仿函数适配器
[STL] 19.算法algorithm
[STL] 20.迭代器适配器
[STL] 21.空间配置器allocator

1. 什么是STL?

标准模板库 - 维基百科,自由的百科全书 (wikipedia.org)

标准模板库(英文:Standard Template Library,缩写:STL),是一个C++软件库,大量影响了C++标准程序库但并非是其的一部分。其中包含4个组件,分别为算法、容器、函数、迭代器。[1]

模板是C++程序设计语言中的一个重要特征,而标准模板库正是基于此特征。标准模板库使得C++编程语言在有了同Java一样强大的类库的同时,保有了更大的可扩展性。

2. STL的优势

  • 功能强大、代码精致

STL由惠普实验室的无数大牛们开发,设计精巧,功能强大,集成了优秀的算法。

  • 高可重用性

ST广泛使用模板泛型编程,代码具有高度的复用性。

  • 高性能、工业强度

ST提供的算法具有工业强度,高性能,用它可开发出性能高效的应用程序。

  • 开源跨平台

STL跨平台,而且开源,开发者可以很容易借鉴与扩展。

3. STL版本?

HP STL

HP STL是Alexandar Stepanov在惠普Palo Alto实验室工作时,与Meng Lee合作完成的。HP STL是C++ STL的第一个实现版本,而且是开放源码。其它版本的C++ STL一般是以HP STL为蓝本实现出来的。

SGI STL

由Silicon Graphics Computer Systems公司参照HP STL实现,主要设计者仍然是STL之父Alexandar Stepanov,被Linux的C++编译器GCC所采用。SGI STL是开源软件,源码可读性甚高。

STLport

为了使SGI STL的基本代码都适用于VC++和C++ Builder等多种编译器,俄国人Boris Fomitchev建立了一个free项目来开发STLport,此版本STL是开放源码的。

P.J.Plauger STL

由P.J.Plauger参照HP STL实现出来,被Visual C++编译器所采用,但不是开源的。

Rouge Wave STL

由Rouge Wave公司参照HP STL实现,用于Borland C++编译器中,这个版本的STL也不是开源的。

4. STL的六大组件

image-20210503121253228

1、容器(Containers):各种数据结构,如Vector,List,Deque,Set,Map,用来存放数据,STL容器是一种Class Template,就体积而言,这一部分很像冰山载海面的比率。

2、算法(Algorithms):各种常用算法如Sort,Search,Copy,Erase,从实现的角度来看,STL算法是一种Function Templates。

3、迭代器(Iterators):扮演容器与算法之间的胶合剂,是所谓的“泛型指针”,共有五种类型,以及其它衍生变化,从实现的角度来看,迭代器是一种将:Operators*,Operator->,Operator++,Operator–等相关操作予以重载的Class Template。所有STL容器都附带有自己专属的迭代器——是的,只有容器设计者才知道如何遍历自己的元素,原生指针(Native pointer)也是一种迭代器。

4、仿函数(Functors): 行为类似函数,可作为算法的某种策略(Policy),从实现的角度来看,仿函数是一种重载了Operator()的Class 或 Class Template。一般函数指针可视为狭义的仿函数。

5、配接器(适配器)(Adapters):一种用来修饰容器(Containers)或仿函数(Functors)或迭代器(Iterators)接口的东西,例如:STL提供的Queue和Stack,虽然看似容器,其实只能算是一种容器配接器,因为 它们的底部完全借助Deque,所有操作有底层的Deque供应。改变Functor接口者,称为Function Adapter;改变Container接口者,称为Container Adapter;改变Iterator接口者,称为Iterator Adapter。配接器的实现技术很难一言蔽之,必须逐一分析。

6、分配器(Allocators):负责空间配置与管理,从实现的角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的Class Template。

​ ——《STL源码剖析》

5. 学习建议

路线:

  • C++数据结构
  • C++模板
  • STL标准库
  • Boost库

资源:

Containers - C++ Reference (cplusplus.com)

cppreference.com

image-20210503121942346

image-20210503133131350

6. 为什么需要迭代器?

#include "stdafx.h"
#include<iostream>
using namespace  std;

/* 思考题一
现在有一个数组  int  arr[5] = { 1,2,3,4,5 };
void   Print(int  *begin, int  *end)
{
//此处编写代码
}
让 Print(arr, arr + 5);  实现打印所有元素?
*/

/*
void   Print(int  *begin, int  *end)
{
   //此处编写代码
   for (int *p = begin; p != end; ++p)//利用指针的++,依次访问每个元素
   {
   	cout << *p << "		";
   }
   cout << endl;
}
*/

/*  思考题二

现在有一个链表,有5个Node节点:

struct   Node
{
int  data;
Node*  next;
};

void   Print(Node *begin, Node *end)
{
//此处编写代码
}

让 Print(pHead,  pTail );  实现打印所有元素?
*/

struct   Node
{
   int  data;
   Node*  next; 
};

ostream & operator <<(ostream&  o,const  Node &node)
{
   o << node.data << endl;
   return   o;
}

/*
void   Print(Node *begin, Node *end)
{
   //此处编写代码
   for (Node *p    =begin;  p!= end ;  p=p->next) //指向下一个节点
   {
   	cout << (*p).data << "		"; 
   }
   cout << endl;
}
*/

//思考题三
//写出一个通用的Print 函数模板
template<class T>
void   Print(T  begin, T  end)
{
   //此处编写代码
   for (T  p = begin; p != end; ++p)
   {
   	cout << *p << "		";
   }
   cout << endl;
}

int main()
{
   //题目一测试
   int  arr[5] = { 1,2,3,4,5 };
   Print(arr, arr + 5); //arr + 5是指向 最后一个元素下一个的位置
   
   //Print<int *>(arr, arr + 5);//调用模板函数


    //题目二测试
   Node  n1;
   n1.data = 11;
   Node  n2;
   n2.data =22;
   Node  n3;
   n3.data = 33;
   Node  n4;
   n4.data = 44;
   Node  n5;
   n5.data = 55;

   n1.next = &n2;
   n2.next = &n3;
   n3.next = &n4;
   n4.next = &n5;
   n5.next = NULL;

   Print(&n1, n5.next);

   return 0;
}

7. 自己实现迭代器iterator

迭代器(iterator)是指针的抽象,它允许程序员以相同的方式处理不同的数据结构(容器)。

迭代器支持的一些基本操作:

  • =:迭代器赋值
  • ==:比较迭代器的相等性
  • !=:比较迭代器的不等性
  • ++:迭代器向后移动
  • --:迭代器向前移动
  • *:解引用,返回迭代器指向的元素
#include "stdafx.h"
#include<iostream>
using namespace  std;

//思考题三
//写出一个通用的Print 函数模板
template<class T>
void   Print(T  begin, T  end)
{
   //此处编写代码
   for (T  p = begin; p != end; ++p)
   {
   	cout << *p << "		";
   }
   cout << endl;
}

//虽然数组不需要迭代器,因为指针就是一种特殊的迭代器
//也可以设计一个数组迭代器
class  ArrayIterator
{
public:
   ArrayIterator(int *p) : _p(p) {}
   ArrayIterator  &  operator++()
   { 
   	++_p;//指针就是++指向下一个
   	return  *this ;
   }

   bool   operator!=(const ArrayIterator & it)
   {
   	return  _p != it._p;
   }

   int&   operator*()
   {
   	return   *_p;
   }
   //private: 
   int * _p;
};

//考虑设计一个中间类,让它支持operator ++,实际实现是链表的p=p->next
//链表迭代器
class  ListIterator
{
public:
   ListIterator(Node *p) : _p(p) {} 
   ListIterator  &  operator++()
   {
   	//链表的真正操作
   	_p = _p->next;
   	return  *this;
   }

   bool   operator!=(const ListIterator & it)
   {
   	return  _p != it._p;
   }

   Node&   operator*( )
   {
   	return   *_p;
   }
//private: 
   Node * _p; 
};
 
int main()
{
   //使用链表迭代器
   ListIterator begin(&n1);
   ListIterator end(n5.next);
   Print(begin, end);

   return 0;
}

8. STL中的5种迭代器

  • 输入迭代器(Input iterator)
  • 输出迭代器(Output Iterator)
  • 前向迭代器(Forward iterator)
  • 双向迭代器(Bidirectional iterator)
  • 随机访问迭代器(Random Access iterator)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-on3WXlhN-1620021492142)(C:\Users\tangx\AppData\Roaming\Typora\typora-user-images\image-20210503133856493.png)]

image-20210503133806278

image-20210503133715845

不同的数据结构(容器)支持的迭代器类型不同,比如数组结构可支持随机访问迭代器,但是链表结构就无法支持,链表往往设计为双向迭代器。

9. STL中的iterator类

iterator类是一个基类模板,可用于从它派生迭代器类。它不提供迭代器具有的任何功能,仅提供了些默认成员类型,这些并不是迭代器类型必须需具备的,它们可能是有用的。

定义:

iterator - C++ Reference (cplusplus.com)

template <class Category, class T, class Distance = ptrdiff_t,
         class Pointer = T*, class Reference = T&>
 struct iterator {
   typedef T         value_type;
   typedef Distance  difference_type;
   typedef Pointer   pointer;
   typedef Reference reference;
   typedef Category  iterator_category;
 };

Category

迭代器所属的类别。 它必须是以下迭代器标签之一:

迭代器标签迭代器的类别迭代器类型
input_iterator_tagInput Iterator输入迭代器
output_iterator_tagOutput Iterator输出迭代器
forward_iterator_tagForward Iterator前向迭代器
bidirectional_iterator_tagBidirectional Iterator双向迭代器
random_access_iterator_tagRandom-access Iterator随机访问迭代器

T

迭代器指向的元素类型。

Distance

键入以表示两个迭代器之间的差异。

Pointer

类型,表示指向迭代器指向的元素的指针。

Reference

类型,表示对迭代器指向的元素的引用。

示例代码:

#include "stdafx.h"
#include<iostream>
#include<iterator>

using namespace  std;

class  A
{
public:
   typedef  int  MYINT;  //内部类型
   using   MYFLOAT = float; //内部类型
};

class MyIterator : public std::iterator<std::random_access_iterator_tag, int>
{
   int* p;
public:
   MyIterator(int* x) :p(x) {}
   MyIterator(const MyIterator& mit) : p(mit.p) {}
   MyIterator& operator++() { ++p; return *this; }
   MyIterator operator++(int) { MyIterator tmp(*this); operator++(); return tmp; }
   bool operator==(const MyIterator& rhs) const { return p == rhs.p; }
   bool operator!=(const MyIterator& rhs) const { return p != rhs.p; }
   int& operator*() { return *p; }
   //int &operator[](int index) { 	return   *(p + index); }
};

int main()
{
   //A::MYINT  a = 100;
   //A::MYFLOAT b = 100;

   //定义一个迭代器对象
   iterator<  input_iterator_tag   , int>  it ;

 	//内部类型:iterator_category代表迭代器类别类型
   cout << typeid(iterator<input_iterator_tag, int>::iterator_category).name() << endl;

   //迭代器指向元素的类型
   cout << typeid( iterator<input_iterator_tag, int>::value_type).name() << endl;

   //迭代器指向元素的指针类型
   cout << typeid(iterator<input_iterator_tag, int>::pointer).name() << endl;

   //迭代器指向元素的引用类型 
   cout << typeid(iterator<input_iterator_tag, int>::reference).name() << endl;

   //迭代器指向元素的指针类型 相减得到的差值类型
   cout << typeid(iterator<input_iterator_tag, int>::difference_type).name() << endl;

   iterator<input_iterator_tag, int>::value_type  a = 100;
   iterator<input_iterator_tag, int>::pointer  pA = &a;
   iterator<input_iterator_tag, int>::reference  b=a;

   cout << a << "		" << *pA << endl;
   b = 200;
   cout << a<<"		"<< b << endl;

   iterator<input_iterator_tag, int>::pointer  pB = &a+2;

   iterator<input_iterator_tag, int>::difference_type  c = pB - pA;
   cout << c << endl;

   //测试自定义的迭代器
   int numbers[] = { 10,20,30,40,50 };
   MyIterator from(numbers);
   MyIterator until(numbers + 5);
   for (MyIterator it = from; it != until; it++)
   	std::cout << *it << ' ';
   std::cout << '\n';
   
   //cout << from[3] << endl;

   MyIterator::value_type  x = 100;

   return 0;
}

10. 序列容器对比

序列容器数据结构底层实现迭代器优缺点
array (c++11)固定数组普通数组、连续内存随机访问迭代器快速随机访问、在中间插入、删除元素效率较低、无法扩容
vector动态数组new 堆内存、连续内存随机访问迭代器快速随机访问、在中间插入、删除元素效率较低、容量可动态增长
deque双端队列一个中央控制器和多个缓冲区、不连续随机访问迭代器快速随机访问、两端插入、删除效率高
list双向链表双向链表、不连续双向迭代器不支持随机访问、可双向遍历、任意位置插入、删除元素效率高
forward_list (c++11)单向链表单向链表、不连续前向迭代器不支持随机访问、单向遍历、任意位置插入、删除元素效率高

11. 容器适配器对比

容器适配器数据结构底层实现迭代器特点
stack底层默认用queue实现,也可以是vectorlist不支持先进后出 FILO
queue队列底层默认用queue实现,也可以是list不支持先进先出 FIFO
priority_queue优先队列底层默认用vector为容器,使用堆heap为处理规则来排序。容器也可以是deque不支持优先级最高(或最低)的元素先出

12. 关联容器对比

序列容器数据结构底层实现迭代器优缺点
set集合红黑树双向迭代器key唯一、有序
map映射红黑树双向迭代器key唯一、有序、value可重复
multiset多重集合红黑树双向迭代器key可重复、有序
multimap多重映射红黑树双向迭代器key可重复、有序、value可重复
unordered_set无序集合哈希表双向迭代器key唯一、无序
unordered_map无序映射哈希表双向迭代器key唯一、无序、value可重复
unordered_multiset (c++11)无序多重集合哈希表双向迭代器key可重复、无序
unordered_multimap (c++11)无序多重映射哈希表双向迭代器key可重复、无序、value可重复
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超级D洋葱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值