以STL的运用角度而言,空间配置器是最不需要介绍的东西,它总是隐藏在一切组件(更具体地说是指容器,container)的背后,默默工作,默默付出。但若以STL的实现角度而言,第一个需要介绍的就是空间配置器,因为整个STL的操作对象(所有的数据)都存放在容器之内,而容器一定需要配置空间以置放资料。

为什么不说allocator是内存配置器而说它是空间配置器呢?因为空间不一定是内存,空间也可以是磁盘或其它辅助存储介质。是的,你可以一个allocator,直接向硬盘取空间,以下介绍的是SGI STL提供的配置器,配置的对象是内存。

空间配置器的标准接口

根据STL的规范,以下是allocator的必要接口:

allocator::value_type
allocator::pointer
allocator::const_pointer
allocator::reference
allocator::const_reference
allocator::size_type
allocator::difference_type allocator::rebind
一个嵌套的class template,class rebind<U>拥有唯一的成员other,那是一个typedef,代表allocator<U> allocator::allocator()
default constuctor allocator::allocator(const allocator&)
copy constructor template<class U>allocator::allocator(const allocator<U>&)
泛化的copy constructor allocator::~allocator()
destructor pointer allocator::address(reference x)const
返回某个对象的地址,算式a.address(x)等同于&x const_pointer allocator::address(const_reference x)const
返回某个const对象的地址,算式a.address(x)等同于&x pointer allocator::allocate(size_type n,const void* =0)
配置空间,足以存储n个T对象,第二参数是个提示,实际上可能会利用它来增进区域性,或完全忽略之 void allocator::deallocate(pointer p,size_type n)
归还先前配置的空间 size_type allocator::max_size() const
返回可成功分配的最大量 void allocator::construct(pointer p,const T& x)
等同于 new((void*)p) T(x) placement new void allocator::destroy(pointer p)
等同于p->~T()

SGI 空间配置器

SGI STL的配置器与众不同,它与标准规范不同。如果要在程序中明白采用SGI配置器,那么应该这样写:

vector<int, std::alloc> iv; //gcc编译器

配置器名字为alloc,不接受任何参数。标准配置器的名字是allocator,而且可以接受参数。比如VC中写法:

vector<int, std::allocator<int> > iv; //VC编译器

SGI STL的每一个容器都已经指定了缺省配置其alloc。我们很少需要自己去指定空间配置器。比如vector容器的声明:

template <class T, class Alloc = alloc>
class vector {
//...
}

SGI标准的空间配置器allocator

其实SGI也定义了一个符合部分标准,名为allocator的配置器,但是它自己不使用,也不建议我们使用,主要原因是效率不佳。它只是把C++的操作符::operator new和::operator delete做了一层简单的封装而已。下面仅仅贴出代码:

#ifndef DEFALLOC_H
#define DEFALLOC_H #include <new.h>
#include <stddef.h>
#include <stdlib.h>
#include <limits.h>
#include <iostream.h>
#include <algobase.h> //仅仅是简单的封装了operator new
template <class T>
inline T* allocate(ptrdiff_t size, T*) {
set_new_handler();
T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
if (tmp == ) {
cerr << "out of memory" << endl;
exit();
}
return tmp;
} //仅仅是简单的封装了operator::delete
template <class T>
inline void deallocate(T* buffer) {
::operator delete(buffer);
} template <class T>
class allocator {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type; pointer allocate(size_type n) {
return ::allocate((difference_type)n, (pointer));
}
void deallocate(pointer p) { ::deallocate(p); }
pointer address(reference x) { return (pointer)&x; }
const_pointer const_address(const_reference x) {
return (const_pointer)&x;
}
size_type init_page_size() {
return max(size_type(), size_type(/sizeof(T)));
}
size_type max_size() const {
return max(size_type(), size_type(UINT_MAX/sizeof(T)));
}
}; class allocator<void> {
public:
typedef void* pointer;
}; #endif

SGI特殊的空间配置器alloc

一般而言,我们所习惯的C++内存配置器操作和释放操作时这样的:

class FOO { ...};
FOO* pf=new FOO; //配置内存,然后构造对象
delete pf; //将对象析构,然后释放内存

这其中的

new算式内含两个阶段操作:

(1)调用::operator new 配置内存

(2)调用FOO::FOO()构造对象内容

delete算式也内含两个阶段操作:

(1)调用FOO::~FOO()对对象析构

(2)调用::operator delete释放内存

为了精密分工,SGI allocator将两个阶段分开

内存配置操作由alloc:allocate负责,内存释放由alloc:deallocate负责;对象构造操作由::contructor()负责,对象析构由::destroy()负责。

STL标准告诉我们,配置器定义在头文件<memory>中,它里面又包括两个文件:

#include <stl_alloc.h>		// 负责内存空间的配置和器释放
#include <stl_construct.h> // 负责对象的构造和析构

内存空间的配置/释放与对象内容的构造/析构,分别落在这两个文件身上。其中<stl_construct.h>定义了两个基本函数:构造用的construct()和析构用的destroy().

下图显示了其结构:

图一 头文件 <memory>结构

构造函数析构的基本工具:construct()和destroy()

下面是<stl_constuct.h>的部分内容:

函数construct()使用了定位new操作符,其源代码:

template <class T1, class T2>
inline void construct(T1* p, const T2& value) {
new (p) T1(value); // 定为new操作符placement new; 在指针p所指处构造对象
}

函数destroy则有两个版本。

第一个版本较简单,接受一个指针作为参数,直接调用对象的析构函数即可,其源代码:

template <class T>
inline void destroy(T* pointer) {
pointer->~T(); // 调用析构函数
}

第二个版本,其参数接受两个迭代器,将两个迭代器所指范围内的所有对象析构掉。而且,它采用了一种特别的技术:依据元素的型别,判断其是否有trivial destructor(无用的析构函数)进行不同的处理。这也是为了效率考虑。因为如果每个对象的析构函数都是trivial的,那么调用这些毫无作用的析构函数会对效率造成影响。

下面看其源代码:

// 以下是 destroy() 第二版本,接受两个迭代器。它会设法找出元素的数值型別,
// 进而利用 __type_traits<> 求取最适当措施。
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
__destroy(first, last, value_type(first));
} // 判断元素的数值型別(value type)是否有 trivial destructor,分别调用上面的函数进行不同的处理
template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destroy_aux(first, last, trivial_destructor());
} // 如果元素的数值型別(value type)有 trivial destructor…
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}//不调用析构函数 // 如果元素的数值型別(value type)有 non-trivial destructor…
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
for ( ; first < last; ++first)
destroy(&*first);//调用析构函数
}

第二版本还针对迭代器为char*和wchar_t*定义了特化版本:

inline void destroy(char*, char*) {}
inline void destroy(wchar_t*, wchar_t*) {}

图二显示了这两个函数的结构和功能。他们被包含在头文件stl_construct.h中。

图二 函数construct()和destroy()示意图

这两个座位构造、析构之用的函数被设计为全局函数,符号STL的规范。此外,STL还规定配置器必须拥有名为construct()和destroy()的两个成员函数。

上述construct()接收一个指针p和一个初值value,该函数的用途就是将初值设定到指针所指的空间上。C++的placement new运算可用来完成这一任务。

destroy()有两个版本,第一版本接受一个指针,准备将该指针所指之物析构掉。这很简单,直接调用该对象的析构函数即可。第二版本接受first和last迭代器,准备将[first,last)范围内的所以对象析构掉。我们不知道这个范围有多大,万一很大,而每个对象的析构函数都无关痛痒(所谓的trivial destructor),那么一次次调用这些无关痛痒的析构函数,对效率是一种伤害。因此,这里先利用value_type()获得迭代器所指对象的型别,再利用_type_traits<T>判断该型别的析构函数是否无关痛痒。若是(_true_type),则什么也不做就结束;若否,(_false_type),这才以循环方式巡访整个范围,并在循环中每经历一个对象就调用一个版本的destroy().

STL——空间配置器(构造和析构基本工具)的更多相关文章

  1. 【转】STL空间配置器

    STL空间配置器(allocator)在所有容器内部默默工作,负责空间的配置和回收.STL标准为空间配置器定义了标准接口(可见<STL源码剖析>P43).而具体实现细节则由各编译器实现版本 ...

  2. 【陪你系列】5 千字长文+ 30 张图解 | 陪你手撕 STL 空间配置器源码

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub https://github.com/rongweihe/MoreT ...

  3. STL空间配置器

    1.什么是空间配置器? 空间配置器负责空间配置与管理.配置器是一个实现了动态空间配置.空间管理.空间释放的class template.以内存池方式实现小块内存管理分配.关于内存池概念可以点击:内存池 ...

  4. stl空间配置器线程安全问题补充

    摘要 在上一篇博客<STL空间配置器那点事>简单介绍了空间配置器的基本实现 两级空间配置器处理,一级相关细节问题,同时简单描述了STL各组件之间的关系以及设计到的设计模式等. 在最后,又关 ...

  5. 咬碎STL空间配置器

    STL空间配置器 一.开场白: 给我的感觉就是,了解是空间配置器的功能,是那么的明了:在看原理,我还是很开心:接下来是360度大转变: 那么长的变量或者函数命名.那么多的宏.不爽,不过,遇上我这种二货 ...

  6. STL空间配置器那点事

    STL简介 STL(Standard Template Library,标准模板库),从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其 ...

  7. STL空间配置器解析和实现

    STL空间配置器的强大和借鉴作用不言而喻,查阅资料,发现了Dawn_sf已经对其有了极其深入和详细的描述,所以决定偷下懒借用其内容,只提供自己实现STL空间配置器的源码,具体解析内容参考:(一)STL ...

  8. STL——空间配置器(SGI-STL)

    一. 空间配置器标准接口 参见<STL源码剖析>第二章-2.1.<memory>文件. 二.具备次配置力的SGI空间配置器 1. SGI STL的配置器与众不同,也与标准规范不 ...

  9. stl空间配置器简介

    1. 符合STL标准的空间配器接口 STL是c++中使用非常广泛的一个标准库,它包含各种有用的容器.而空间配置器作为STL各种容器的背后的核心,负责容器内部内存的分配和释放.不过空间配置器可以分配的也 ...

随机推荐

  1. Grunt:多个css,js,进行单独压缩

    module.exports = function (grunt) { // 构建任务配置 grunt.initConfig({ //读取package.json的内容,形成个json数据 pkg: ...

  2. Unity3D动态加载外部资源

    最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBundle,其实两者本质上我理解没有什么区别.Resources ...

  3. wechat-php-sdk

    wechat-php-sdk 微信公众平台php版开发包 支持消息加解密方式的明文模式.兼容模式.安全模式 支持自动接入微信公众平台(步骤) 功能模块 Wechat (处理自动接入.获取与回复微信消息 ...

  4. 1.2机器学习基础下--python深度机器学习

    1. 机器学习更多应用举例: 人脸识别   2. 机器学习就业需求:      LinkedIn所有职业技能需求量第一:机器学习,数据挖掘和统计分析人才      http://blog.linked ...

  5. Extjs通过structs2生成树结构

    Extjs为我们提供了强大的树的生成方式,我们不必通过原始的js去生成树形结构.在这里我做了一个简单的树结构生成.代码如下,同时在后台使用了fastjson-1.1.15.jar的jar包生成json ...

  6. 简化 Django

    http://www.oschina.net/translate/simplifying-django 尽管Django的流行和普及, 一些开发者仍然认为她是一个过时的web开发框架, 仅仅适合内容丰 ...

  7. Color the Fence

    Codeforces Round #202 (Div. 2) B:http://codeforces.com/problemset/problem/349/B 题意:给你一些颜料,然后你可以用这些颜料 ...

  8. Atlantis

    poj1151:http://poj.org/problem?id=1151 题意:求矩形面积的并题解:扫描线加线段树 #include<iostream> #include<cst ...

  9. github 托管代码两分钟教程【转载,亲测可行】

    http://blog.csdn.net/duxinfeng2010/article/details/8654690 出自以上地址 本篇文章介绍的是如何将工程代码托管到上面:如果你还没注册GitHub ...

  10. ArrayList的toArray

    ArrayList提供了一个将List转为数组的一个非常方便的方法toArray.toArray有两个重载的方法: 1.list.toArray(); 2.list.toArray(T[]  a); ...