STL源码探究
文章平均质量分 62
STL(Standard Template Library),C++模板库。通过阅读它的源码,能够让我们更加熟练的使用STL,并且学到到各种容器,迭代器,算法的实现以及泛型思维。
UKey_
勿以浮沙筑高台
展开
-
SGISTL源码探究-配接器
前言关于配接器,之前在分析序列式容器时就已经分析过queue和stack,它们也是配接器的一种,通过使用其他容器的接口然后自己加以修饰,形成另外一种容器。除了应用在容器上之外,还有关于应用在仿函数上的配接器,就如我们上一小节分析仿函数时里面的例子一样。还有一种是应用在迭代器上的配接器,等下我们会进行分析。应用于容器的配接器关于这一部分,之前已经分析过queue和stack的实现了,这里就不做赘述了。原创 2017-09-26 19:18:49 · 349 阅读 · 0 评论 -
SGISTL源码探究-仿函数
前言本小节将介绍STL六大组件之一的仿函数(又称函数对象),在前面分析算法部分时,我们其实已经见识到了仿函数的大量使用(各算法的另一个版本,传入comp仿函数)。关于仿函数的实现,其实就是重载了()运算符,然后调用,所以又称函数对象。接下来我们就来分析该部分。仿函数虽然函数指针也可以完成将各种操作当作参数传入,但是之所以出现了仿函数,是因为函数指针并不能满足STL对抽象的要求,也不能和其他组件很好的原创 2017-09-26 13:14:52 · 771 阅读 · 0 评论 -
SGISTL源码探究-stl_algo.h中的排序算法
前言在本小节中,我们将分析STL中算法组件的最后一部分:排序算法。排序算法分为内部排序和外部排序。而内部排序中常见的有八种排序算法:直接插入排序、希尔排序、简单选择排序、堆排序、冒泡排序、快速排序、归并排序、桶排序。在SGISTL实现的sort算法中,并不是简单的使用快速排序作为排序算法,而是交叉使用了插入排序、堆排序以及快速排序,并且做了一定的优化,并且只接受迭代器类型为RandomAccessI原创 2017-09-25 20:21:29 · 762 阅读 · 0 评论 -
SGISTL源码探究-stl_algo.h中的排列算法
前言在本小节中,我们将分析stl_algo.h中的排列算法。可能你使用过STL中的next_permutation求过全排列。比如这样:#include <iostream>#include <algorithm>#include <string>using namespace std;int main(){ string str; cin >> str; sort(原创 2017-09-24 16:31:39 · 763 阅读 · 1 评论 -
SGISTL源码探究-stl_alog.h中的二分查找算法
前言在上一小节中我们分析了stl_algo.h中的部分算法。本小节中我们将继续分析其中关于二分查找类的算法,即lower_bound、upper_bound、binary_search、equal_range。这些算法相比于上一节的要稍微复杂些,如果你对二分法比较了解,这一小节应该也很轻松。STL中的二分查找算法lower_bound使用该函数的前提是传入的区间必须有序,这也是二分法的唯一要求。lo原创 2017-09-24 16:26:14 · 572 阅读 · 0 评论 -
SGISTL源码探究-stl_algo.h中的基础算法
前言在上一小节中我们分析了stl_algobase.h中的算法,虽然都是一些比较基础的算法,但是了解其中的泛化以及特化的思想是很重要的。在本小节中我们将分析stl_algo.h中的算法,里面的大部分算法也很基础,比较复杂的我们放在另外的小节分析。各算法的实现for_each遍历[first, last),每个元素使用仿函数f进行处理。注意传入的迭代器类型是InputIterator,所以可能不支持赋原创 2017-09-23 17:59:39 · 670 阅读 · 0 评论 -
SGISTL源码探究-stl_algobase.h中的算法
前言在上一小节中,我们分析了stl_numeric.h中的算法部分。在本小节中,我们将分析stl_algobase.h文件中的算法,里面实现的都是一些比较基本的算法,比如equal、fill、max、swap等,可以从中学习到泛型的思想。stl_algobase.h中的算法iter_swap该函数的作用是交换迭代器指向的两个元素。注意迭代器的类型必须是ForwardIterator及其派生类。 并原创 2017-09-22 21:23:05 · 1011 阅读 · 0 评论 -
SGISTL源码探究-stl_numeric.h中的数值算法
前言上一小节中,我们对stl中的算法部分做了一个大致的说明。本小节我们将进入到其中关于数值算法的部分,都在stl_numeric.h文件中。废话不多说,直接看源码吧。stl_numericaccumulate注意该算法要求了迭代器的类型至少是InputIterator类型,如果是OutputIterator类型,肯定是不行的。 用于计算元素总和,init加上[first, last)范围内的元素之原创 2017-09-20 16:03:40 · 526 阅读 · 0 评论 -
SGISTL源码探究-STL中的算法(前言)
前言我们目前已经分析了STL中的六大组件的一半。剩下的还有算法、仿函数、配接器,已经分析了配置器、迭代器、容器部分。接下来进入到剩下的三大组件中最复杂的算法部分,关于STL中的算法,它实现了很多功能,但是分析这部分的时候我只打算选择一部分比较经典的算法进行分析(因为这部分的算法实在是太多了),只要掌握了分析的方法,以后自己遇到其他的STL算法或者感兴趣的,直接阅读源码就行了。概览大致分类STL中的算原创 2017-09-20 13:58:12 · 476 阅读 · 0 评论 -
SGISTL源码探究-关联式容器:hash_multimap
前言本小节将介绍hash_multimap的源码实现,它与hash_map的不同与map和multimap的不同类似,所以我们可以一边分析它们的不同之处,一边顺便复习hash_map。hash_multimap的实现定义及数据结构#ifndef __STL_LIMITED_DEFAULT_TEMPLATEStemplate <class Key, class T, class HashFcn =原创 2017-09-19 11:06:32 · 1000 阅读 · 0 评论 -
SGISTL源码探究-关联式容器:hash_multiset
前言在上几节中我们分析了hashtable以及hash_set还有hash_map的实现,hash_set以及hash_map都不允许有重复的key存在,而hashtable提供了允许插入重复元素的操作insert_equal,对应实现的则是hash_multiset以及hash_multimap。 接下来我们先看看hash_multiset的实现,它和hash_set的实现很类似。hash_mu原创 2017-09-19 10:50:39 · 980 阅读 · 0 评论 -
SGISTL源码探究-关联式容器:hash_map
前言在上一小节,我们分析了hash_set的实现,本小节我们将针对hash_map进行分析。它大部分也是调用的hashtable,不过它与map的区别也是因为底层的实现不同,所以map具有自动排序功能,而hash_map没有。 接下来我们来看看hash_map是如何实现的。hash_map的实现还是分为定义部分及其数据结构、构造函数、常用操作这三部分。关于hash_map的迭代器,是使用的hash原创 2017-09-19 09:35:30 · 718 阅读 · 0 评论 -
SGISTL源码探究-关联式容器:hash_set
前言在上小节中,我们已经分析了hashtable的实现,它只是hash_set和hash_map的预备知识。在本小节中,我们将进入到关联式容器hash_set的源码中,一窥究竟。hash_set的实现由于hash_set并没有自己定义的迭代器,所以接下来我们先对它的定义部分以及数据结构进行分析,然后是它的构造函数以及常用操作等。定义部分及数据结构#ifndef __STL_LIMITED_DEFAU原创 2017-09-19 08:58:21 · 762 阅读 · 0 评论 -
SGISTL源码探究-STL中的hashtable(下)
前言在上一小节中,我们介绍了hashtable的基本概念,以及分析了在STL中,hashtable的迭代器、采用的数据结构及它的构造/析构函数,还有部分成员函数。 在本小节中,我们讲继续分析剩下的一些操作,如插入、删除、复制等。插入操作关于插入操作,有大量的重载版本,我们只分析比较关键的函数,其他的可以随便看看。/* insert_unique,插入元素(不允许重复) * 首先先判断加上新增元素原创 2017-09-18 21:07:22 · 1037 阅读 · 0 评论 -
SGISTL源码探究-STL中的hashtable(上)
前言关于哈希表部分,哈希函数以及解决哈希冲突的方法都有很多种,在本文中不会详细去讨论哈希表的大量知识,而是以SGISTL实现的hashtable为主,所以如果对hashtable不太了解,建议先去网上查阅相关的资料。这里只给出基本的概念。了解哈希表原理基本概念哈希表(hashtable,也称散列表):根据key来访问元素的一种数据结构。它可能先经过哈希函数计算出key,然后通过key访问表中对应ke原创 2017-09-18 19:49:02 · 1339 阅读 · 0 评论 -
SGISTL源码探究-关联式容器:multimap
前言在本小节中我们将分析multimap的实现。它和map的最大的不同之处在于,map不允许重复的键值插入,而multimap允许重复的键值插入,对应到底层的实现,multimap调用的是insert_equal进行插入操作,而map调用的是insert_unique进行插入操作。 另外还有一个不同之处是multimap不支持下标运算,这个也很合理,因为可能有重复键,访问下标时可能就对应了多个值,原创 2017-09-17 16:11:09 · 658 阅读 · 0 评论 -
SGISTL源码探究-关联式容器:multiset
前言multiset和set的实现十分相似,除了允许键值重复之外,几乎一样。而实现允许键值重复这一功能的是红黑树中提供的操作insert_equal,接下来我们就进入到它的源码,顺便也复习一下set,看看到底和set有什么不同。multiset的实现#ifndef __STL_LIMITED_DEFAULT_TEMPLATEStemplate <class Key, class Compare =原创 2017-09-17 15:39:02 · 731 阅读 · 0 评论 -
SGISTL源码探究-关联式容器:map
前言在本小节中,我们将分析map容器,它与set最大的不同就是,它是key-value型的,而set的key和value是同一个。map和set底层都由红黑树实现,map上存储的都是pair,即键值对,存储到红黑树中时,排序依据的就是pair的第一个元素。 之前提到在set中,不允许更改元素的值,而在map中,key值同样不允许修改,但是value可以修改,因为value不涉及到map整体内部结构原创 2017-09-17 10:46:18 · 792 阅读 · 0 评论 -
SGISTL源码探究-pair的实现
前言本小节将对pair进行源码分析,它经常用于map的实现中,而我们下一节就会分析map的源码,所以有必要在这一小节中,先把pair分析了。 pair的作用就是将两个数据合成一个,而这两个数据可以是不同的类型,这就非常符合key-value这种键值对形式的存储了,接下来我们就进入到它的源码之中。pair的实现pair的实现代码很短,100行不到,但是对map的实现确实提供了非常大的便利。它提供的操原创 2017-09-17 10:44:08 · 673 阅读 · 0 评论 -
SGISTL源码探究-关联式容器:set
前言在本小节中,我们将分析set容器,它的特性是,所有的元素会根据键值自动在set内部排序,set的键值就是实值,并且不允许两个相同值的元素在set中存在。并且set的元素值不能通过迭代器进行改变,因为一旦改变,就意味着set内部有序就被打破了。 set实现的机制和map一样,同样以红黑树作为底部实现,而关于set的操作,也只用调用红黑树提供的接口就行了。set的实现定义部分#ifndef __S原创 2017-09-16 19:32:08 · 578 阅读 · 0 评论 -
SGISTL源码探究-STL中的红黑树(下)
前言在上一小节中,我们针对SGISTL中的红黑树的分析完成了一半,接下来的各种操作才是重点的部分。通过以下分析可以了解到红黑树是如何保持平衡的。(STL中实现红黑树定义的重载版本函数太多,我舍去了一部分定义,直接对其声明进行分析,不然篇幅太长)红黑树的常用操作clear上一小节中,析构函数调用了clear,它的实现如下 void clear() { if (node_count != 0)原创 2017-09-16 17:32:10 · 770 阅读 · 0 评论 -
SGISTL源码探究-STL中的红黑树(上)
前言本小节将进入到SGISTL的红黑树部分。关于红黑树,是一种比较复杂的数据结构,但是并不是特别难。如果对红黑树不太了解,可以去网上查阅相关的资料,因为本文的主要目的是分析STL中的红黑树的源码,和普通的红黑树略有不同,还要复杂一些,需要有一定的基础。基本概念首先什么是二叉搜索树,二叉搜索树即符合任何一个节点的值一定大于其左子树的任何一个节点的值并且小于其右子树的任何一个节点的值(节点的值不允许重复原创 2017-09-16 14:35:17 · 1571 阅读 · 0 评论 -
SGISTL源码探究-优先级队列
前言在上一小节中,我们分析了heap算法,其实是为了priority_queue优先级队列做准备,它也不是容器,而是配接器,因为它是通过vector来实现的,然后封装了一些vector提供的接口供自己使用而形成的。 优先级队列,即一种特殊的队列,但是它内部的元素没有按照传统队列的先进先出这种顺序排序,而是按照元素的权值进行排列,所以执行出队操作时,出队的是权值最大的元素。priority_queu原创 2017-09-15 20:01:07 · 634 阅读 · 0 评论 -
SGISTL源码探究-大根堆heap
前言小根堆和大根堆的概念在数据结构都讲过,等下简单的过一下就行了。在SGISTL的实现中,它并不作为一种容器,而是一系列的算法,用于给priority_queue提供支持,使优先级队列能够体现其优先级。 在SGISTL实现heap中,采用的数据结构并不是使用二叉树实现,而是采用隐式表达,即使用数组表示一个堆。能用数组表示这是因为堆总是一棵完全二叉树(没有节点漏洞):即若设二叉树的高度为h,除第 h原创 2017-09-15 19:14:05 · 693 阅读 · 0 评论 -
SGISTL源码探究-queue配接器
前言在本小节中,我们将分析queue,即队列,这种数据结构的元素符合先进先出(FIFO)的条件。它同stack一样,不算是一种容器,而是采用一种容器(SGISTL中默认是deque)作为底层结构并修改部分接口为自己所用的配接器。queue同stack一样,不支持元素的遍历操作,因此并不需要迭代器。它的所有接口都依靠底层的容器实现。queue源码定义部分#ifndef __STL_LIMITED_DE原创 2017-09-14 15:09:48 · 663 阅读 · 0 评论 -
SGISTL源码探究-stack配接器
前言迄今为止,我们已经分析了vector、list、deque容器,而stack并不是一个容器,它是依据一种容器作为底层结构,然后根据自己的要求改变容器提供的接口。而在SGISTL的实现中,stack默认是以deque为底部结构,并且封装了一些接口并且stack中没有迭代器,它也并不需要,因为栈这种结构,并不支持遍历等操作,而出栈入栈这些操作,直接使用deque提供的操作,然后自己封装一下就行了。原创 2017-09-14 15:08:54 · 621 阅读 · 0 评论 -
SGISTL源码探究-deque容器(下)
前言在上一小节中我们介绍了deque容器实现的大致思路以及它内部使用的一些机制,比如存储元素的空间其实是由指针数组里面的指针指向的等。 在本小节中,我们主要介绍一些deque容器封装的一些常用的操作,比如push_back、push_front、insert、erase、clear等。这些函数无非就是根据迭代器内部的first、last、cur的位置来决定进行操作。 所以有必要再重温一下firs原创 2017-09-13 15:06:30 · 476 阅读 · 0 评论 -
SGISTL源码探究-deque容器(上)
前言deque容器比list以及vector容器都要复杂的多。它用两端都有开口的线性空间来存储数据,这样的话在首部操作数据的效率比vector高,以及虽然它的迭代器是random_access_iterator_tag型的,但是并不是原生指针,需要封装,比list和vector的迭代器都要复杂。它实现的机制是利用二级指针,维护一个指针数组map,然后数组中存储的指针指向具体的线性空间。 map并不原创 2017-09-12 21:39:23 · 681 阅读 · 0 评论 -
SGISTL源码探究-list容器(下)
前言在上一小节我们主要介绍了list容器的迭代器及数据结构还有结点的分配及释放,构造和析构函数以及一些简单的操作。在本小节中我们将继续分析list容器提供的一些常见操作。insert之前关于list的构造函数的源码中都调用了insert函数,接下来,我们就来分析它,要记得,STL规定insert函数都是向前插入。 它有多个重载版本。在指定位置前插入一个结点iterator insert(itera原创 2017-09-11 20:10:38 · 410 阅读 · 0 评论 -
SGISTL源码探究-list容器(上)
前言在本小节中,我们将介绍序列式容器之一的list,它采用的数据结构是环状双向链表,而前面分析的vector是线性存储的。list对于插入数据,删除数据的效率很高,并且迭代器不会轻易失效。但是它的迭代器并不像vector那样是原生指针,所以它的类型可能并不是random_access_iterator_tag型,并且需要自己内嵌那五种相应型别。那么接下来,我们就正式进入到list容器的源码。 首先原创 2017-09-11 13:13:44 · 391 阅读 · 0 评论 -
SGISTL源码探究-vector容器(下)
前言在本小节中,我们将接着上一下节,继续分析关于vector的内存控制,及一些常用操作部分。vector的内存控制引入在上一节中我们给出了一个简单的例子,借助这个例子已经大约明白了vector管理内存的机制。接下来,我们就来看看push_back这以操作。push_backvoid push_back(const T& x) { /* 判断是否到vector最大容量 * 若没有,则构造该对原创 2017-09-10 12:42:05 · 570 阅读 · 0 评论 -
SGISTL源码探究-vector容器(上)
前言vector容器应该算是最常用的容器之一了,它是序列式容器的一种,即元素可被排序。vector被称为动态数组,可以随着元素的加入,内部的大小自行扩充。可能你对vector的机制有所耳闻,比如每次扩充时,扩充当前容量的2倍等。接下来,我们就进入到它的源码来看看vector是如何实现的。我们将它分为四部分进行分析,vector的迭代器,vector的内存分配及构造,vector的内存控制以及一些常用原创 2017-09-09 17:22:38 · 680 阅读 · 5 评论 -
SGISTL源码探究-traits技法
前言在上一小节中,我们提到了迭代器的相应型别,其中之一的便是value_type,它是根据函数模板的参数推导机制获取到的。但是需要考虑一种另外的情况,当数据类型不是对象的时候,这种时候很明显无法再在类中typedef T value_type了,而是一个普通的int *指针,那么我们如何得到它的value_type。只用将原生指针进行特殊处理就行了(模板的偏特化),这里就引入了traits,专门用来原创 2017-09-09 11:47:40 · 521 阅读 · 0 评论 -
SGISTL源码探究-迭代器的类型
前言迭代器主要用于将数据结构与算法粘合在一起。当我们平时使用迭代器的时候,主要的操作无非是使用operator *以及operator ->,之所以它表现的像指针,就是因为对这两个操作符进行了重载。而不同的容器因为其存储的数据结构不同,比如访问其成员时,若是单链表,则不支持向前遍历,因此缺少跟其他迭代器的一些功能;而一些算法也会根据数据结构能否支持某种操作(比如随机访问)来选择最有效率的做法。迭代器原创 2017-09-08 17:31:02 · 430 阅读 · 0 评论 -
SGISTL源码探究-默认使用的配置器
前言在上节中我们主要分析了chunk_alloc函数,在本小节中,我们先对第一级配置器和第二级配置器的使用做个整理,随后再介绍空间配置器提供的初始化未初始化空间的接口。默认使用的配置器当我们使用vector这些容器时,其实是默认指定了使用的空间配置器的,比如vector在源码中的模板头是这样的template <class T, class Alloc = alloc>,默认指定了配置器为alloc原创 2017-09-07 18:25:29 · 391 阅读 · 0 评论 -
SGISTL源码探究-内存池
前言在本小节中我们将讲解chunk_alloc函数,从内存池中取空间给free_list,这便是chunk_alloc需要做的事。chunk_alloc引入该函数的大致逻辑是: 若内存池的容量够需求,则直接返回给free_list;如果不够,能分多少就分多少给free_list;如果一个都分不了,则看看内存池还有没有剩余的零头,如果有的话,分给其他free_list,然后对内存池进行补充容量(ma原创 2017-09-07 13:02:30 · 478 阅读 · 0 评论 -
SGISTL源码探究-第二级配置器
前言在本小节中我们将主要分析第二级配置器申请/释放内存的做法,它的主要实现机制是靠free_lists以及内存池来实现。内存池放在单独的一小节进行讲解。第二级配置器(__default_alloc_template)引入第二级配置器要比第一级复杂的多。它的做法是,如果要求申请的空间大于128bytes,则让第一级配置器去做,如果小于128bytes,则由内存池提供,而第二级配置器只负责维护16个fr原创 2017-09-06 19:28:48 · 393 阅读 · 0 评论 -
SGISTL源码探究-第一级配置器
前言在本小节中,我们主要介绍第一级配置器的做法。第一级配置器(__malloc_alloc_template)引入第一级配置器用于处理大于128bytes的情况,即申请大的内存。直接使用malloc和free申请释放即可。(现在有可能不是以128bytes为标准了,不过源码本来就是主着学习的目的,不管现在是多少,至少你能从中学到思想就够了)深入源码 //这里是一个模板偏特化技术,即int类型,可以原创 2017-09-06 15:40:23 · 457 阅读 · 0 评论 -
SGISTL源码探究-空间配置器
前言该系列的博客主要是看《STL源码剖析》以及一些相关的博客得到的心得及一些自己的理解整理而成,如有错误请指出,谢谢。遵照《STL源码剖析》一书的顺序进行分析,先是STL的空间配置器方面。allocator引入事实上,我们在学习C++的时候,接触到空间配置器应该就是allocator,它用于申请一块未初始化的空间,这是因为new会执行两个动作,首先申请空间,其次调用其构造函数。但是如果有些空间根本就原创 2017-09-06 15:32:27 · 470 阅读 · 0 评论