C++ STL 漫谈(c++中的stl到底指的什么)
wptr33 2025-06-04 02:13 22 浏览
标准模板库(Standard Template Library,STL)是惠普实验室开发的一个函数库和类库。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室工作时所开发出来的。
STL是一个模板类库和模板函数库。STL并不仅仅是一个库,它更是一种优秀的思想以及一套约定。
STL包含三大组件:容器、算法和迭代器。容器用于容纳和组织元素;算法针对容器执行操作;迭代器则用于访问容器中的元素。这些都不是什么新东西,许多传统的程序库也都包含类似的组件,并且许多程序库也都是采用模板实现而成。STL的优秀思想体现在:容器与在容器上执行的算法之间无需彼此了解,这种戏法是通过迭代器实现的。
模板类库包含了一些常用数据结构(在STL中叫容器),这些模板类库中通常包含了一个迭代器模板类,用于提供给通常算法来遍历容器内部元素。STL容器只包含了线性结构数据,存储单一元素的容器称为序列容器,存储关联元素(用pair模板类封装的key与value)称为关联容器。容器可以动态增长、可以是顺序存储或链式存储,为查找效率需要,也可以是以红黑树作为底层的数据存储结构或哈希存储。
模板函数库包含了一些适用容器的通用算法。
Stepanov并认为,虽然C++中的继承功能可以表示泛型设计,但终究有个限制。虽然可以在基础类型(superclass)定义算法和接口,但不可能要求所有对象皆是继承这些,而且庞大的继承体系将减低虚拟(virtual)函数的运行效率,这便违反C++所强调的“效率”原则。(C++标准库和STL都是没有所谓基类Object这一语法机制。)
事实上,C++的模板,本身即是一套复杂的宏语言(macro language),宏语言最大的特色为:所有工作在编译时期就已完成。显式的声明函数模板之实例,与直接通过C++的重载功能隐式声明,结果一样,并无很大区别,只是前者加重程序员的负担,使得程序变得累赘。
STL 将“在数据上执行的操作”与“要执行操作的数据分开”,分别以如下概念指代:
容器:数据结构的概念,包含、放置数据的地方(模板类,封装了与容器结合紧密的一些操作容器元素的函数)。
迭代器:在容器中指出一个位置、或成对使用以划定一个区域,用来限定操作所涉及到的数据范围。是数据结构与算法的中间层,是封装在容器模板类内的模板类,迭代器类封装的是外部类(容器类)元素指针,迭代器通常用做算法参数或类似指针用途。
算法:可以在绝大部分容器执行的操作(以适当类别的迭代器为参数)。
1 容器(数据结构)
STL容器是对数据结构的一种抽象,以类模板的方式实现而成。由于具有不同的数据结构,因此不同的容器以不同的方式来组织其元素,以便对存取或操纵进行优化。
标准模板库包含了序列容器(sequence containers)与关系容器(associative containers)。
序列容器包括vector,list,forward_list,deque和array等。
关联容器包括set,multiset,map,multimap,unordered_set,bitset和valarray等。
2 迭代器
迭代器类似于指针(实际上指针就是一种STL迭代器)。像指针一样,迭代器可以指向序列中的一个元素,也可以对其进行解引用(dereference) , 以便获得它所指向的对象的值。我们可以像操纵指针那样操纵迭代器,使其指向序列中不同的元素(其背后中指针操作符的重载)。STL迭代器既可以是预定义的指针,也可以是用户自定义的类类型,当然,这种类型需要重载适当的操作符,以便与预定义指针拥有相同的使用语法。
迭代器是泛化的指针,通过使用迭代器,开发者可以操作数据结构而无需关心其内部实现。根据迭代器的操作方式的不同,迭代器分为输入、输出、前向、双向、随机访问迭代器。
demo code(自定义链表和迭代器):
#include <list>
#include <iostream>
#include <string>
using namespace std;
template <typename T> // 1 数据组成
struct singleList
{
singleList<T>* next;
T data;
singleList() // 创建表头
{
this->next = NULL;
}
singleList(T data) // 创建节点
{
this->data = data;
this->next = NULL;
}
singleList(T data, singleList<T>* next) // 创建节点+连接节点的功能
{
this->data = data;
this->next = next;
}
};
template <typename T>
class List{
public:
List() // 构造函数的万金油写法
{
// 构造函数作用:构造对象
// 初始化基本数据成员,描述最初状态
sizeList = 0;
listHeadNode = listTailNode = NULL; // 对应STL库list的迭代器begin()和end()返回的指针
}
void push_back(T data) // 基本行为:插入操作
{
singleList<T>* newNode = new singleList<T>(data);
if (listHeadNode == NULL)
listHeadNode = newNode;
else
listTailNode->next = newNode;
listTailNode = newNode; // 两种情况的尾节点都指向新节点
//listTailNode->next=NULL;
}
void push_front(T data)
{
singleList<T>* newNode = new singleList<T>(data);
if (listHeadNode == NULL)
listTailNode = newNode;
else
newNode->next = listHeadNode;
listHeadNode = newNode; // 两种情况的头节点都指向新节点
}
//测试行为:遍历结构(打印输出)
void printList()
{
singleList<T>* pMove = listHeadNode;
while (pMove)
{
cout << pMove->data << " ";
pMove = pMove->next;
}
cout << endl;
}
singleList<T>* begin()
{
return listHeadNode;
}
singleList<T>* end()
{
return listTailNode->next;
// 表示链表最后那个位置,NULL的位置
}
//万金油行为
int size() const
{
return sizeList;
}
bool empty() const
{
return sizeList == 0;
}
public:
class iterator // 迭代器是一个类中类,用类的对象来模仿指针,需包含一个对象指针和重载操作符
{
public:
// 实现begin对iterator对象的初始化
void operator=(singleList<T>* pMove)
{
// 实质初始化对象中的属性,而不是对象的例子
this->pMove = pMove;
}
singleList<T>* getData()
{
return pMove;
}
bool operator!=(singleList<T>* pMove)
{
return this->pMove != pMove;
}
// ++实质是链表的一个移动指针走到一下一个节点
iterator& operator++() // 前置++重载
{
this->pMove = this->pMove->next; // 链式存储指针的移动
return (*this);
}
T operator*()
{
return this->pMove->data;
}
protected:
singleList<T>* pMove;
};
protected:
// 抽象数据成员
// 遵循的原则:抽象出来的属性能够便于描述行为(成员函数)
singleList<T>* listHeadNode;
singleList<T>* listTailNode;
//万金油属性
int sizeList;
};
int main()
{
cout<<"STL中list的使用:"<<endl;
list<string> mylist;
mylist.push_back("I");
mylist.push_back("Love");
mylist.push_back("you");
list<string>::iterator listIter;
for (listIter = mylist.begin(); listIter != mylist.end(); ++listIter)
{
cout << *listIter << " ";
}
cout << endl;
cout<<"自定义list的使用:"<<endl;
List<string> myList;
myList.push_back("I");
myList.push_back("Love");
myList.push_back("you");
//myList.printList();
List<string>::iterator myIter;
//myIter=myList.begin();
//cout<<myIter.getData()->data<<endl;
for (myIter = myList.begin(); myIter != myList.end(); ++myIter)
{
cout << *myIter << " ";
}
cout << endl;
cin.get();
return 0;
}
/* 输出
STL中list的使用:
I Love you
自定义list的使用:
I Love you
*/
3 算法
STL提供了一些常见的算法,如排序和搜索等。这些算法与数据结构的实现进行了分离。因此,也可对自定义的数据结构使用这些算法,只需让这些自定义的数据结构拥有算法所预期的迭代器。
STL算法是对函数的一种抽象,采用函数模板实现。大多数STL算法用于处理一个或多个序列的值,其中每一个序列由一对有序的迭代器定义。其中第一个迭代器指向序列的第一个元素,第二个迭代器则指向序列最后一个元素之后的那个位置(而不是最后一个元素)。如果两个迭代器指向同一个位置,那么它们就定义了一个空白序列。
迭代器提供了一种使容器与算法协同工作的机制。一个容器可以生成一对迭代器来指定一个元素序列(可以是全部元素,也可以只是一个子区间),而算法则对该序列进行操作。采用这种方式,容器和算法可以紧密地协作,同时还可以保持彼此不知情。
4 函数对象和适配器
除了容器、算法和迭代器之外,STL还定义了大量的辅助性功能。算法和容器可以采用函数指针和函数对象根据需要进行定制,而这些函数对象又可以通过形形色色的函数对象适配器(function object adapter)进行配接和联合。容器也可以利用容器适配器(container adapter)进行配接,从而将容器的接口修改为栈、队列或优先队列。
4.1 函数对象
狭义的函数对象即重载了操作符()的类的实例,而广义来讲所有可用 x(...) 形式调用的 x 都可称为函数对象、或曰可调用对象。
4.2 适配器
适配器(Adaptor)为一个模板类,用于提供接口映射。
5 约定(convention)
STL对约定(convention)有着很强的依赖。容器和函数对象必须通过一套标准的嵌套类型名字对其自身进行描述。容器和函数对象适配器均要求成员函数具有特定的名字并包含特定的类型信息。算法要求传递给它的迭代器支持特定的操作并能够识别是什么样的操作。当使用或扩展STL时,如果弃约定于不顾,那么同时也就放弃了美梦成真的希望。遵守约定,将拥有简单而轻松的生活。
STL约定并未指明具体的实现细节,但对实现指定了效率方面的约束。此外,由于STL是一个模板库,许多优化和性能调整可以在编译期进行。前面提到的命名和信息约定会对编译期优化起到重要的作用。一般而言,STL的效率可以与专家手写代码的效率相雄美,而明显比普通程序员和程序员小组的手写代码胜出一筹。另外,使用STL可以使代码变得更清晰,更易于维护。
6 萃取器(traits)
有时仅仅知道对象的类型是不够的,通常来说,某些与对象类型有关的信息对于处理对象来说不可或缺。
traits类是一个关于某个类型的信息的集合。然而,与嵌套的容器信息不同的是,traits类独立于它所描述的类型。
有关traits类的一个常见的应用,是在泛型算法和不遵从算法期望的约定的类型之间,放入一个遵从约定的中间层。根据类型的traits编写算法。在一般情况下通常假设存在某种约定。
-End-
相关推荐
- MySQL进阶五之自动读写分离mysql-proxy
-
自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...
- 3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?
-
引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...
- 一文由浅入深带你完全掌握MySQL的锁机制原理与应用
-
本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...
- 验证Mysql中联合索引的最左匹配原则
-
后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...
- MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)
-
目录1.索引基础...
- 你会看 MySQL 的执行计划(EXPLAIN)吗?
-
SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...
- MySQL 从入门到精通(四)之索引结构
-
索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...
- mysql总结——面试中最常问到的知识点
-
mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...
- mysql总结——面试中最常问到的知识点(2)
-
首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...
- MySQL基础全知全解!超详细无废话!轻松上手~
-
本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...
- 深入剖析 MySQL 中的锁机制原理_mysql 锁详解
-
在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...
- Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析
-
引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...
- MySQL基础篇:DQL数据查询操作_mysql 查
-
一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...
- MySql:索引的基本使用_mysql索引的使用和原理
-
一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
如何将AI助手接入微信(打开ai手机助手)
-
Java面试必考问题:什么是乐观锁与悲观锁
-
redission YYDS spring boot redission 使用
-
SparkSQL——DataFrame的创建与使用
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)