高效STL--非标准散列容器

STL是建立在泛化之上的。数组泛化为容器,参数化了所包含的对象的类型。函数泛化为算法,参数化了所用的迭代器的类型。指针泛化为迭代器,参数化了所指向的对象的类型。STL中的六大组件:容器、算法、迭代器、配置器、适配器、仿函数。

这六大组件中在容器中分为序列式容器和关联容器两类,正好作为STL源码剖析这本书的内容。迭代器是容器和算法之间的胶合剂,从实现的角度来看,迭代器是一种将operator*、operator->、operator++、operator—等指针相关操作予以重载的class template.所有STL容器都附带有自己专属的迭代器,因为只有容器设计者才知道如何遍历自己的元素。仿函数是一种重载了operator()的class或class template,一般函数指针可视为狭义的仿函数。配接器是一种用来修饰容器或仿函数或迭代器接口的东西,例如STL提供的queue和stack,虽然看似容器,其实只能算是一种容器配接器,因为他们的底部完全借助deque,所有操作都由底层的deque供应。改变functor接口者,成为funciton
adapter;改变container接口者,成为container adapter;改变iterator接口者,成为iterator adapter。

在STL内部定义了几类迭代器,但是在使用的时候,根据迭代器的使用方法可以将迭代器分为输入、输出迭代器、前向、双向迭代器、随机存取迭代器。

STL所实现的,是依据泛型思维架设起来的一个概念结构。这个以抽象概念为主体而非以实际类为主体的结构,形成了一个严谨的接口标准。在此接口之下,任何组件都有最大的独立性,并以所谓迭代器胶合起来,或以所谓配接器互相配接,或以所谓仿函数动态选择某种策略。

对于空间配置器,在std中使用的allocator配置器,但是在STL中不是使用的标准配置器而是使用的SGI自定义的配置器alloc。因为标准中的allocator配置器效率不是很高。因为allocator配置器只是把New和delete做了一层简单的包装。

STL中的空间配置,std::alloc

对于一个平常的操作

ClassFoo {  };

Foo*fp = new Foo;

Deletefp;

上述过程包括连个阶段,调用new分配内存,调用Foo()构造对象。Delete中也包括两个阶段,调用~Foo()析构对象,调用delete释放内存。为了分工明确,STL 中将这两个阶段分开来操作。内存配置操作由alloc::allocate()负责,内存释放操作由alloc::deallocate()负责;对象构造操作由::construct()负责,对象析构操作由::destroy()负责。配置器定义于<memory>之中,SGI <memory>内含 stl_alloc.h stl_construct.h 
前者负责内存空间的配置与释放,后者负责对象的构造与析构。

考虑到小区块可能造成的内存破碎问题,SGI设计了双层的配置器,第一级直接使用Malloc()和free(),第二级则视情况来判断使用什么策略,当配置区块超过128KB,直接使用第一级配置器;当配置区块下雨128BK,使用辅助的memory pool整理方式,而不再求助于第一级配置器。无论使用第一级还是第二级配置器,都封装成了不接收参数的alloc,然后有简单的封装成simple_alloc类。这个类使得空间配置器对外有了标准接口。这个类中只有四个static的函数,对应着alloc中一对函数

Template<class T,class Alloc>

Class simple_alloc

{

Public:

StaticT* allocate(size_t n)

{  return 0 == n? 0:(T*)Alloc::allocate(n*sizeof(T));}

StaticT* allocate(void)

{  return (T*) Alloc::allocate(sizeof (T)); }

Staticvoid deallocate(T* p,size_t n)

{  if(0 !+ n)Alloc::deallocate(p,n*sizeof(T));}

Staticvoid deallocate(T* p)

{  Alloc::deallocate(p,sizeof(T));}

};

内部的四个static成员函数对应着空间配置器中的实现的函数,然后有全局的构造和析构哈数,这两者的结合把资源管理集合起来了。

我们只需要知道,在SGI STL中是有专门的空间配置器的,空间配置器管理这内存的分配和释放,也就是说内存配置器中初始化了两个函数分别管理内存的释放和分配,内存的分配时有优化的,首先看申请的内存是否够大,如果很大,那么就直接调用第一级的配置器,也就是使用malloc,如果不超过128KB,那么就使用第二级的配置器,第二级的配置器中保存了一系列的链表,从保存的链表内找一个合适的内存块来满足要求,这个配置器是不接收模板参数的alloc,对外又做了简单的封装—simple_alloc,此类也是封装那一对函数。同时有一对全局的函数,负责对象的构造和析构,也就是destroy()和construct().

有了空间配置器alloc和上层封装simple_alloc之后,如何使用呢?

Template<class T,calss Alloc= alloc>

Class vector

{

Public:

TypedefT value_type;

Protected:

Typedefsimple_alloc<value_type,Alloc> data_allocator;

};

到现在为止,我们知道了空间配置器是alloc,在容器作为默认的空间配置器,为了拥有更好的对外接口,封装成了simple_alloc类,里面封装了alloc中的两个重要函数,分别用户与内存的分配和释放,同时拥有了两个全局函数用于对象的构造和析构—construct()  destroy()。

其实,还有三个全局函数,uninitialized_copy()  uninitialized_fill()  uninitialized_fill_n(),分别对应高层次函数copy()fill() fill_n()---这些都是STL算法。这三个全局函数从名字也可以判断其作用。

上述的uninit*三个函数都有都了泛化 特化的技术,为了更高效的处理。在泛化的分支中使用到了STL中的那三个函数。

总结:在空间配置中,要明白SLT使用了自己定义的alloc配置器,这个配置器分为两级,提供了内存的释放和分配的工作,然后定义了五个全局函数,两个全局是用来对象的构造和析构,三个全局是更高效的初始化内存存在的。为了更好的使用alloc.对外封装了simple_alloc这个类,在各个容器中都是使用这一个类。

时间: 2024-10-11 22:49:00

高效STL--非标准散列容器的相关文章

Java散列和散列码的实现

转自:https://blog.csdn.net/al_assad/article/details/52989525 散列和散列码 ※正确的equals方法应该满足的的条件: ①自反性:x.equals(x) 一定返回true: ②对称性:y.euqlas(x)为true,那么x.equals(y)一定为true: ③传递性:x.equals(y)为true,y.euqlas(z)为true,则z.equals(x)为true: ④一致性:如果x,y中用于等价比较的信息没有变化,那么无论调用y.

容器深入研究 --- 散列与散列码(一)

通常的: 当标准类库中的类被作用HashMap的键.它用的很好,因为它具备了键所需的全部性质. 当你自己创建用作HashMap的键的类,有可能会忘记在其中放置必须的方法,而这时通常会犯的一个错误. 例如:考虑一个天气系统,将Groundhog对象与Prediction对象联系起来. class Groundhog { protected int number; public Groundhog(int n) { number = n; } public String toString() { r

c语言 &lt;除法散列法&gt; 高效 HashTable Dictionary

c语言 <除法散列法> 高效 HashTable Dictionary  ,不管集合大小,任意长度根据key查询都只是一次寻址左右,so 最快时间复杂度为O1! 先上代码,明天写原理注释! HashDictionary.h #define CM_STR_HASHFUNC_CONSTANT 31 #define KEYSIZE 40 struct Entry { int hashCode; int next; char key[KEYSIZE]; void* value; }; struct H

HashMap实现 Hash优化与高效散列

OverView Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynch

容器深入研究 --- 散列与散列码(三)

如何覆盖hashCode(): 明白了如何散列之后,编写自己的hashCode()就更有意义了. 首先,你无法控制bucket数组的下标值的产生.这个值依赖于具体的HashMap对象的容量,而容量的改变与容器的充满程度和负载因子有关.hashCode()生成的结果,经过处理后称为桶位的下标. 设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值.如果在将一个对象用put()添加进HashMap时产生一个hashCode()值,而用get()

标准模板库(STL)学习探究之vector容器

标准模板库(STL)学习探究之vector容器  C++ Vectors vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据.为了可以使用vector,必须在你的头文件中包含下面的代码:#include <vector>构造函数. Vectors 包含着一系列连续存储的元素,其行为和数组类

容器深入研究 --- 散列与散列码(二)

为速度而散列: SlowMap.java说明了创建一个新的Map并不困难.但正如它的名称SlowMap所示,它不会很快,如果有更好的选择就应该放弃它.它的问题在于对键的查询,键没有按照任何特定的顺序保存,所以只能使用简单的线性查询,而线性查询是最慢的查询方式. 散列的价值在于速度: 散列使得查询得以快速进行.由于瓶颈在于键的查询速度,因此解决方案之一就是保持键的排序状态,然后使用Collections.binarySearch()进行查询. 散列则更进一步,它将键保存在某处,以便能够很快的找到.

Python3标准库:hashlib密码散列

1. hashlib密码散列 hashlib模块定义了一个API来访问不同的密码散列算法.要使用一个特定的散列算法,可以用适当的构造器函数或new()来创建一个散列对象.不论使用哪个具体的算法,这些对象都使用相同的API. 1.1 散列算法 由于hashlib有OpenSSL提供“底层支持”,所以OpenSSL库提供的所有算法都可用,包括: md5 sha1 sha224 sha256 sha384 sha512 有些算法在所有平台上都可用,而有些则依赖于底层库.这两种算法分别由algorith

STL源码剖析---关联容器

标准关联容器分为set和map两大类,包括multiset和multimap,这些容器的底层机制都是RB-tree.标准之外的关联容器有hashtable 以及以此hash table为底层机制而完成的hash_set(散列集合) hash_map(散列映射表) hash_multiset  hash_multimap. 序列和关联容器各自的内部关系是内含的,例如heap内含一个vector,priority_quehe内含一个heap,stack和queue都内含一个deque,set/map