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

智能推荐

C程序设计——考研复试_考研复试c语言程序设计-程序员宅基地

文章浏览阅读7.2k次,点赞14次,收藏189次。C程序设计目录文章目录C程序设计目录前言第一章——程序设计和C语言总结前言本人2022考研,复试用书为《C程序设计》第四版,我用的第五版,没有差别。先整体复习,再重难点突破。大多都为基础概念。_考研复试c语言程序设计

教育领域中学数学NER数据集_中国初中数学知识数据集-程序员宅基地

文章浏览阅读749次,点赞10次,收藏11次。相对于其他领域来讲,教育领域尤其是学科方面缺乏公开的数据集,故本人及团队从数据筛选到清洗以及标注,构建了中学数学实体识别数据集。_中国初中数学知识数据集

java报错5:javax.mail.AuthenticationFailedException_exception in thread "main" javax.mail.authenticati-程序员宅基地

文章浏览阅读2.4k次。Exception in thread &quot;main&quot; javax.mail.AuthenticationFailedException: failed to connectat javax.mail.Service.connect(Service.java:322)at javax.mail.Service.connect(Service.java:172)at javax.mail.Ser..._exception in thread "main" javax.mail.authenticationfailedexception: failed

ITK/VTK对DICOM文件的读取_sitk读取mnc文件-程序员宅基地

文章浏览阅读6.6k次。在ITK中,对dicom的读取使用的是GDCM库,而在VTK库中使用的是直接是DicomFileReader库。在ITK中,读取DICOM文件的程序示例如下:_sitk读取mnc文件

求二维数组a[3][4]中的最大数和最小数_在二维数组中查找最大值和最小值并输出-程序员宅基地

文章浏览阅读2.2k次。求二维数组a[3][4]中的最大数和最小数#include<stdio.h>void main(){ int a[3][4],i,j,max,min; //输入 for(i=0;i<3;i++){ for(j=0;j<4;j++){ scanf("%d",&a[i][j]); } } //找最大数、最小数 min=a[0][0]; max=a[0][0]; for(i=0;i<3;i++){ for(j=0;j<4;j++){_在二维数组中查找最大值和最小值并输出

JUC锁——共享锁和可重入读写锁_共享锁 支持重入吗-程序员宅基地

文章浏览阅读452次。概要  JUC中的共享锁有CountDownLatch、CyclicBarrier、Semaphore、ReentrantReadWriteLock等,本章会以ReentrantReadWriteLock为蓝本对共享锁进行说明。ReadWriteLock 和 ReentrantReadWriteLock介绍  ReadWriteLock,顾名思义,是读写锁。它维护了一对相关的锁——“读取锁”..._共享锁 支持重入吗

随便推点

每日力扣002——两数之和(c#)_c# 力扣两数之和-程序员宅基地

文章浏览阅读96次。第一眼看出两个for嵌套,因为只有一解,所以满足条件时可以直接return。public class Solution{ public int[] TwoSum(int[] nums, int target) { for (int i = 0; i < nums.Length; i++) { for (int j = 0; j < nums.Length; j++) { ..._c# 力扣两数之和

二手房房价预测案例 -- 代码实现_基于数据挖掘算法根据房屋面积实现房价预测代码的方法-程序员宅基地

文章浏览阅读2.7k次,点赞2次,收藏32次。1. 最小二乘线性(OLS)线性回归模型:lm1 = ols("price ~ dist_丰台+dist_朝阳+dist_东城+dist_海淀+dist_西城+school+subway+floor_middle+floor_low+style_new+roomnum+AREA", data=dat1).fit()2. 逐个检验变量的解释力度import statsmodels.api as smfrom statsmodels.formula.api import olsprint("_基于数据挖掘算法根据房屋面积实现房价预测代码的方法

1788:Pell数列_pellshuliec++-程序员宅基地

文章浏览阅读2.2k次,点赞2次,收藏3次。1788:Pell数列查看提交统计提问总时间限制: 3000ms 内存限制: 65536kB描述 Pell数列a1, a2, a3, …的定义是这样的,a1 = 1, a2 = 2, … , an = 2 * an − 1 + an - 2 (n > 2)。 给出一个正整数k,要求Pell数列的第k项模上32767是多少。 输入 第1行是_pellshuliec++

javascript将canvas的ImageData转Image和DataURL_js getimagedata 转为 image-程序员宅基地

文章浏览阅读490次。【代码】javascript将canvas的ImageData转Image和DataURL。_js getimagedata 转为 image

Visual Studio 2022 从下载安装到如何使用的全面讲解 (图文详解)_vs2022-程序员宅基地

文章浏览阅读1.7k次,点赞44次,收藏49次。Visual Studio 2022做为一款集成开发软件是一款非常好用的代码开发软件,而且可调试让我们快速找到错误!和理解代码出了那些问题!可以说是初学者的必备利器!_vs2022

推荐文章

热门文章

相关标签