对使用STL有一定经验的程序员的一个常见问题是,“如何编写STL分配器?”。 编写STL分配器并不是特别困难——只有两个成员函数是有趣的:allocate()和deallocate()。 然而,STL分配器必须满足许多其他要求(由C++国际标准第20.1.5节给出,ISO/IEC 1488∶2003),并且这样做的代码大约需要80条编辑线。 从需求中找出代码是非常困难的,但是一旦你看到了代码,就很容易了。
一些程序员尝试从std::allocator派生。 我反对这样做;麻烦太多了。 查看std::分配器的实现也很痛苦。
因此,我编写了一个示例STL分配器,它的用途是包装malloc()和free(),我很有想象力地称之为Mallocator。 我仔细地实现了实际生产代码中需要的所有整数溢出检查等。 我已经详尽地评论了Mallocator的哪些部分是样板文件(对于所有、几乎所有或所有无状态分配器都是通用的),以及您必须自定义哪些部分。 希望这能揭开STL分配器实现的神秘面纱:
C:温度>类型mallocator.cpp
//所有分配器都需要以下标头。
#包括
#包括
#include
//以下标题包含Mallocator使用的内容。
#包括
#包括
#包括
//以下标题包含main()使用的内容。
#包括 // 对于std::列表
模板
公众:
//对于几乎所有的分配器,以下内容都是相同的。
typedef T*指针;
typedef const T*常量指针;
typedef T&reference;
typedef const T&constu参考;
typedef T值类型;
typedef sizeu t sizeu type类型;
typedef ptrdiff t difference类型;
T*地址(T&r)常量{
返回&r;
}
常量T*地址(常量T&s)常量{
退货&s;
}
sizeu t maxu size()常量{
//以下内容是为了独立于
//大小的定义,以避免有符号/无符号警告。
返回(静态u cast
}
//对于所有分配器,以下条件必须相同。
模板
typedef Mallocator其他;
};
布尔接线员=(const Mallocator和其他)const{
回来(*此==其他);
}
空心构造(T*const p,const T&T)const{
void*const pv=静态铸造
新(pv)T(T);
}
无效销毁(T*const p)const;//定义如下。
//当且仅当从*中分配存储时返回true
//可以从其他方取消分配,反之亦然。
//对于无状态分配器,总是返回true。
布尔运算符==(const Mallocator&other)const{
返回true;
}
//默认构造函数、复制构造函数、重新绑定构造函数和析构函数。
//对于无状态分配器为空。
Mallocator(){}
Mallocator(const Mallocator&){}
模板
~Mallocator(){}
//以下内容对于每个分配器都是不同的。
分配(const size)const{
//Mallocator打印诊断信息以演示
//它在做什么。真正的分配器不会这样做。
std::cout<<“分配”<
<<“尺寸”<<尺寸(T)<<“<<标准::endl;
//未指定allocate(0)的返回值。
//Mallocator返回NULL以避免依赖
//关于malloc(0)的实现定义的行为
//(实现可以定义malloc(0)返回NULL,
//在这种情况下,下面的错误分配检查将触发)。
//在这种情况下,所有分配器都可以返回NULL。
如果(n==0){
返回NULL;
}
//所有分配器都应包含整数溢出检查。
//标准化委员会建议std::lengthu error
//在整数溢出的情况下抛出。
如果(n>maxu size()){
throw std::length_error(“Mallocator
}
//Mallocator包装malloc()。
void*const pv=malloc(n*sizeof(T));
//如果内存分配失败,分配器应该抛出std::bad u alloc。
如果(pv==NULL){
throw std::bad_alloc();
}
返回静态u cast
}
void deallocate(T*const p,const size)常量{
//Mallocator打印诊断信息以演示
//它在做什么。真正的分配器不会这样做。
std::cout<<“取消分配”<
<<“尺寸”<<尺寸(T)<<“<<标准::endl;
//Mallocator包装免费()。
游离(p);
}
//以下内容对于忽略提示的所有分配器都是相同的。
模板
返回分配(n);
}
//分配器不需要是可分配的,所以
//所有分配器都应该有一个未实现的私有
//赋值运算符。请注意,这将触发
//默认情况下关闭(在墙下启用)警告C4626
//“无法生成赋值运算符,因为
//基类赋值运算符在“”中不可访问
//STL头,但是这个警告没有用。
私人:
Mallocator&operator=(常量Mallocator&);
};
//编译器错误使它相信p->~T()没有引用p。
#如果定义u MSCu版本
#pragma警告(推送)
#pragma warning(disable:4100)//未引用的形式参数
#结束
//对于所有分配器,destroy()的定义必须相同。
模板
p->~T();
}
#如果定义u MSCu版本
#pragma警告(pop)
#结束
int main(){
使用名称空间标准;
cout<<“构造l:”<
列表
cout<
l、 推回(1729);
cout<
l、 推吧