一、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> // 负责对象的构造和析构
下图显示了其结构:
1、对象的建构和结构函数construct()和destroy()
下图显示了这两个函数的结构和功能。他们被包含在头文件stl_construct.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编程技法(这里有介绍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)();
这个在《effctive c++》第二版条款7中有详细说明。
new_handler set_new_handler(new_handler p) throw();
下面列出第一级配置器__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源码剖析》这本书上的,自己捋一下,思路更加清晰。
文章浏览阅读412次。2、文本格式输入,通过在地理化文本中增加参数定义地理格式。from 重点场景边框2023 LIMIT 3。1、地理化字段直接转换。_postgis将txt转换为geometry格式
文章浏览阅读462次,点赞2次,收藏2次。在动手做一件事之前,先推演一番。_事前推演
文章浏览阅读3.4k次,点赞2次,收藏12次。什么是SaaS? 本篇文章有点长,但看完之后相信你就会对sass有一个清晰的认知。_saas
文章浏览阅读360次。浙大PAT习题集_c语言如何竖着输出ilovegplt
文章浏览阅读1.6w次,点赞7次,收藏16次。命令查找要回退到的提交的 SHA-1 值或引用名称。使用 “mixed” 模式,这将保留提交之后的更改,但将其放置在暂存区中。命令是危险的操作,它会丢弃提交历史记录,并且如果已经将更改推送到远程仓库,则可能会导致问题。这些命令将创建一个新的提交,将先前提交的更改撤销,并将更改应用于代码库。此命令将显示提交记录列表,每个提交记录都有一个唯一的 SHA-1 值和提交消息。注意,这将删除提交历史记录,并且如果已经将更改推送到远程仓库,则可能会导致问题。选项来阻止自动提交,并在撤销所有所需的提交后手动提交更改。_git回退到上一次提交
文章浏览阅读1.9k次。1. 医学影像学医学影像学Medical Imaging,是研究借助于某种介质(如X射线、电磁场、超声波等)与人体相互作用,把人体内部组织器官结构、密度以影像方式表现出来,供诊断医师根据影像提供的信息进行判断,从而对人体健康状况进行评价的一门科学,包括医学成像系统和医学图像处理两方面相对独立的研究方向。仪器主要包括X光成像仪器、C..._mni空间
文章浏览阅读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
文章浏览阅读71次。Cobbler自动化部署cobbler简介Cobbler可以用来快速建立Linux网络安装环境,它已将Linux网络安装的技术门槛,从大专以上文化水平,成功降低到了初中水平,连补鞋匠都能学会。网络安装服务器套件Cobbler(补鞋匠)从前,我们一直在装机民工这份很有前途的职业。自打若干年前Red Hat推出了Kickstart,此后我们顿觉身价增倍。不再需要刻了光盘一台一台的安装L...
文章浏览阅读139次。如果多层(z-index不同)div同样大小,或者最底层的div大小比上层的大,而且底层div包含了img标签,其上层div右键菜单在ie上会默认为img所在层的菜单,造成本身的菜单无法弹出。有点绕,可以到web.qq.com测试(至少本日志发布时间该问题还存在),在桌面空白的地方右键其他浏览器可以正常弹出菜单,而ie却不行。如下面的页面,正常会弹出div20,ie则弹出div0。<..._ie下右键时会阻塞定时器
文章浏览阅读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求角点坐标
文章浏览阅读824次,点赞5次,收藏19次。EasyPlayer.js:打造简洁高效的HTML5视频播放器项目地址:https://gitcode.com/tsingsee/EasyPlayer.jsEasyPlayer.js 是一个轻量级、高度可定制的HTML5视频播放器框架,由Tsingsee团队开发并维护。该项目旨在简化HTML5视频播放器的开发过程,让开发者能够快速构建功能丰富的自定义播放器,适用于各种网站和应用。项目简介E..._easyplayer.js
文章浏览阅读525次,点赞3次,收藏2次。设计激光器的时候,TEC制冷必不可少。那么如何选用合适的TEC呢?_tec 选型