SGI STL空间配置器-第一级空间配置器-程序员宅基地

技术标签: 内存  STL  

一、SGI STL配置器简介
如果要了解STL的实现,必须要了解空间配置器,因为整个STL的操作对象都放在容器之内,而容器一定需要配置空间以存放数据等资料。allocator叫做空间配置器而不是内存配置器,因为空间不一定是内存,也可以是磁盘或者其他辅助存储介质。可以写一个allocator直接向硬盘取空间。当然这里介绍的allocator配置的是内存。
二、SGI标准的空间配置器
其实SGI也定义了一个符合部分标准,名为allocator的配置器,但是它自己不使用,也不建议我们使用,主要原因是效率不佳。它只是把C++的操作符::operator new和::operator delete做了一层简单的封装而已。下面是代码,可以看出空间配置器的标准接口,提供了默认的构造器、复制、析构等接口。另外这里的SGI版本的allocator仅仅对底层的内存配置/释放行为(::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>  


template <class T>  
inline T* allocate(ptrdiff_t size, T*) {  
    set_new_handler(0);  
    T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));  
    if (tmp == 0) {  
    cerr << "out of memory" << endl;   
    exit(1);  
    }  
    return tmp;  
}  


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)0);  
    }  
    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(1), size_type(4096/sizeof(T)));   
    }  
    size_type max_size() const {   
    return max(size_type(1), size_type(UINT_MAX/sizeof(T)));   
    }  
};  

class allocator<void> {  
public:  
    typedef void* pointer;  
};  

#endif 

三、SGI特殊的空间配置器alloc
一般而言,我们所习惯的C++内存配置操作和释放操作是这样的:

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

通常,C++中用new操作符来分配内存都包括两个阶段(如上):
(1)调用::operator new配置内存
(2)调用构造函数Foo::Foo()来构造对象内容

同理,delete操作也包括两个阶段:
(1)调用析构函数Foo::~Foo()将对象析构
(2)调用::operator delete释放内存

为了精密分工,SGI allocator将两个阶段分开:
内存配置操作由alloc:allocate负责,内存释放由alloc:deallocate负责;对象构造操作由::contructor()负责,对象析构由::destroy()负责。
配置器定义在头文件中,它里面又包括两个文件:

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

下图显示了其结构:
图一 头文件 <memory>结构

1、对象的建构和结构函数construct()和destroy()
下图显示了这两个函数的结构和功能。他们被包含在头文件stl_construct.h中。
图二 函数construct()和destroy()示意图

函数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编程技法(这里有介绍http://blog.csdn.net/mmshixing/article/details/51657168):依据元素的型别,判断其是否有trivial destructor(无用的析构函数)进行不同的处理。这也是为了效率考虑。因为如果每个对象的析构函数都是trivial的,那么调用这些毫无作用的析构函数会对效率造成影响。
下面看其源代码:

//destroy()第二个版本,接受两个迭代器。此函数设法找出元素的数值型别  
//然后利用__type_trivial<>求取最适当措施。  
Template <class ForwardInterator>  
Inline void destroy(ForwardInterator first,ForwardInterator last, T*)  
{  
    Typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;  
    __destroy_aux(first, last, trivial_destructor());  
}  

//下面是对__destroy_aux的重载,第三个参数分别为__true_type、__false_type  
//如果元素的数值型别(value type)有non-trivial 函数  
Template <class ForwardIterator>  
Inline void  
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {  
    for( ; first < last ; ++first)  
        destroy(&*first);  //对[first, last)范围的多有对象析构掉!  

}  

//如果元素的数值型别(value type)有trivial 函数  
Template <class ForwardIterator>   
Inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}//什么也不做  

2、空间的配置和释放,std::alloc
对象构造前的空间分配和析构后的空间释放,定义在头文件

template<class T, class Alloc>  
class simple_alloc {  

public:  
    static T *allocate(size_t n)  
                { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }  
    static T *allocate(void)  
                { return (T*) Alloc::allocate(sizeof (T)); }  
    static void deallocate(T *p, size_t n)  
                { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }  
    static void deallocate(T *p)  
                { Alloc::deallocate(p, sizeof (T)); }  
};  

SGI STL容器全部是使用这个simple_alloc接口。

第一级和第二级配置器之间的关系如下图所示:
图三 第一级配置器和第二级配置器
第一级和第二级配置器的包装接口和运用方式如下:
这里写图片描述

第一级配置器__malloc_alloc_template剖析
第一级配置器直接使用malloc(),free(),realloc()等C函数执行实际的内存配置、释放、重配置操作,并实现出类似C++ new handler机制。它有独特的out-of-memory内存处理机制:在抛出std::bad_alloc异常之前,调用内存不足处理例程尝试释放空间,如果用户没有定义相应的内存不足处理例程,那么还是会抛出异常。详细实现见函数oom_malloc(),oom_realloc()。
内存不足处理例程保存在函数指针__malloc_alloc_oom_handler里面。内存不足处理函数,由程序猿自己通过new-handler自己设计,在头文件中这样定义
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
这个在《effctive c++》第二版条款7中有详细说明。

下面列出第一级配置器__malloc_alloc_template代码:

#if 0   
#   include <new>   
#   define  __THROW_BAD_ALLOC throw bad_alloc   
#elif !defined(__THROW_BAD_ALLOC)   
#   include <iostream.h>   
#   define  __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1)   
#endif   

// malloc-based allocator. 通常比稍后介绍的 default alloc 速度慢,   
//一般而言是 thread-safe,并且对于空间的运用比较高效(efficient)。   
//以下是第一级配置器。   
//注意,无「template 型别参数」。至于「非型别参数」inst,完全没派上用场。  
template <int inst>     
class __malloc_alloc_template {   

private:   
//以下都是函式指标,所代表的函式将用来处理内存不足的情况。   
// oom : out of memory.   
static void *oom_malloc(size_t);   
static void *oom_realloc(void *, size_t);   
static void (* __malloc_alloc_oom_handler)();   

public:   

static void * allocate(size_t n)   
{   
    void  *result =malloc(n);//第一级配置器直接使用 malloc()   
    // 以下,无法满足需求时,改用 oom_malloc()   
    if (0 == result) result = oom_malloc(n);   
    return  result;   
}   
static void deallocate(void *p, size_t /* n */)   
{   
free(p); //第一级配置器直接使用 free()   
}   

static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)   
{   
    void  *  result  =realloc(p, new_sz);//第一级配置器直接使用 rea  
    // 以下,无法满足需求时,改用 oom_realloc()   
    if (0 == result) result = oom_realloc(p, new_sz);   
    return  result;   
}   

//以下模拟 C++的 set_new_handler(). 换句话说,你可以透过它,   
//指定你自己的 out-of-memory handler   
static void (* set_malloc_handler(void (*f)()))()   
{   
    void  (*  old)()  =  __malloc_alloc_oom_handler;   
__malloc_alloc_oom_handler = f;   
    return(old);   
}   
};   

// malloc_alloc out-of-memory handling   
//初值为 0。有待客端设定。   
template <int inst>   
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;   

template <int inst>   
void * __malloc_alloc_template<inst>::oom_malloc(size_t n)   
{   
    void  (* my_malloc_handler)();   
    void  *result;   

    for (;;)  {   

//不断尝试释放、配置、再释放、再配置…   
my_malloc_handler = __malloc_alloc_oom_handler;   
        if  (0  ==  my_malloc_handler)  {  __THROW_BAD_ALLOC; }   
        (*my_malloc_handler)();//呼叫处理例程,企图释放内存。   
        result = malloc(n);  //再次尝试配置内存。   
        if  (result)  return(result);   
    }   
}   

template <int inst>   
void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)   
{   
    void  (* my_malloc_handler)();   
    void  *result;   
       for (;;)  {  //不断尝试释放、配置、再释放、再配置…   
my_malloc_handler = __malloc_alloc_oom_handler;   
        if  (0  ==  my_malloc_handler)  {  __THROW_BAD_ALLOC; }   
        (*my_malloc_handler)();//呼叫处理例程,企图释放内存。   
        result = realloc(p, n);//再次尝试配置内存。   
        if  (result)  return(result);   
    }   
}   

//注意,以下直接将参数 inst指定为 0。   
typedef __malloc_alloc_template<0> malloc_alloc;   

以上为STL空间配置器及第一级配置器__malloc_alloc_template详细内容,大部分是《STL源码剖析》这本书上的,自己捋一下,思路更加清晰。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mmshixing/article/details/51658358

智能推荐

postgis 坐标体系及文本转geometry格式转换_postgis将txt转换为geometry格式-程序员宅基地

文章浏览阅读412次。2、文本格式输入,通过在地理化文本中增加参数定义地理格式。from 重点场景边框2023 LIMIT 3。1、地理化字段直接转换。_postgis将txt转换为geometry格式

【以终为始】为什么说做事之前要先进行推演?_事前推演-程序员宅基地

文章浏览阅读462次,点赞2次,收藏2次。在动手做一件事之前,先推演一番。_事前推演

深度详解什么是SaaS(软件即服务)-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏12次。什么是SaaS? 本篇文章有点长,但看完之后相信你就会对sass有一个清晰的认知。_saas

浙江大学PAT解题集7-2 I Love GPLT_c语言如何竖着输出ilovegplt-程序员宅基地

文章浏览阅读360次。浙大PAT习题集_c语言如何竖着输出ilovegplt

git 撤回操作_git回退到上一次提交-程序员宅基地

文章浏览阅读1.6w次,点赞7次,收藏16次。命令查找要回退到的提交的 SHA-1 值或引用名称。使用 “mixed” 模式,这将保留提交之后的更改,但将其放置在暂存区中。命令是危险的操作,它会丢弃提交历史记录,并且如果已经将更改推送到远程仓库,则可能会导致问题。这些命令将创建一个新的提交,将先前提交的更改撤销,并将更改应用于代码库。此命令将显示提交记录列表,每个提交记录都有一个唯一的 SHA-1 值和提交消息。注意,这将删除提交历史记录,并且如果已经将更改推送到远程仓库,则可能会导致问题。选项来阻止自动提交,并在撤销所有所需的提交后手动提交更改。_git回退到上一次提交

医疗图像基础知识_mni空间-程序员宅基地

文章浏览阅读1.9k次。1. 医学影像学医学影像学Medical Imaging,是研究借助于某种介质(如X射线、电磁场、超声波等)与人体相互作用,把人体内部组织器官结构、密度以影像方式表现出来,供诊断医师根据影像提供的信息进行判断,从而对人体健康状况进行评价的一门科学,包括医学成像系统和医学图像处理两方面相对独立的研究方向。仪器主要包括X光成像仪器、C..._mni空间

随便推点

leetcode 64. 最小路径和(棋盘Dp)_棋盘最短路径java dp-程序员宅基地

文章浏览阅读412次。给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。说明:每次只能向下或者向右移动一步。示例:输入:[ [1,3,1], [1,5,1], [4,2,1]]输出: 7解释: 因为路径 1→3→1→1→1 的总和最小。通过次数63,718提交次数98,361class Solution {public:..._棋盘最短路径java dp

Cobbler--自动化部署-程序员宅基地

文章浏览阅读71次。Cobbler自动化部署cobbler简介Cobbler可以用来快速建立Linux网络安装环境,它已将Linux网络安装的技术门槛,从大专以上文化水平,成功降低到了初中水平,连补鞋匠都能学会。网络安装服务器套件Cobbler(补鞋匠)从前,我们一直在装机民工这份很有前途的职业。自打若干年前Red Hat推出了Kickstart,此后我们顿觉身价增倍。不再需要刻了光盘一台一台的安装L...

ie右键菜单bug-程序员宅基地

文章浏览阅读139次。如果多层(z-index不同)div同样大小,或者最底层的div大小比上层的大,而且底层div包含了img标签,其上层div右键菜单在ie上会默认为img所在层的菜单,造成本身的菜单无法弹出。有点绕,可以到web.qq.com测试(至少本日志发布时间该问题还存在),在桌面空白的地方右键其他浏览器可以正常弹出菜单,而ie却不行。如下面的页面,正常会弹出div20,ie则弹出div0。&lt..._ie下右键时会阻塞定时器

opencv python 角点坐标的提取_opencv求角点坐标-程序员宅基地

文章浏览阅读5.4k次,点赞2次,收藏47次。https://blog.csdn.net/zhu_hongji/article/details/81235643图像特征描述角点检测原理ret,labels,stats,centroids=cv2.connectedComponentsWithStats(dst)import cv2 import numpy as npimport matplotlib.pyplot as pltimg =cv2.imread('E:/python/ha.png') imgray=cv2.cvtColo_opencv求角点坐标

EasyPlayer.js:打造简洁高效的HTML5视频播放器-程序员宅基地

文章浏览阅读824次,点赞5次,收藏19次。EasyPlayer.js:打造简洁高效的HTML5视频播放器项目地址:https://gitcode.com/tsingsee/EasyPlayer.jsEasyPlayer.js 是一个轻量级、高度可定制的HTML5视频播放器框架,由Tsingsee团队开发并维护。该项目旨在简化HTML5视频播放器的开发过程,让开发者能够快速构建功能丰富的自定义播放器,适用于各种网站和应用。项目简介E..._easyplayer.js

激光器温控TEC选型设计_tec 选型-程序员宅基地

文章浏览阅读525次,点赞3次,收藏2次。设计激光器的时候,TEC制冷必不可少。那么如何选用合适的TEC呢?_tec 选型