pool库概述

pool库概述:
 boost.pool库基于简单分隔存储思想实现了一个快速,紧凑的内存池库,不仅能够管理大量的对象,还可以被用做STL的内存分配器。某种程度上讲,它近似于一个小型的垃圾回收机制,在需要大量地分配/释放小对象时很有效率,而且完全不需要考虑delete.

 pool库包含四个组成部分:最简单的pool,分配类实例的object_pool,单件内存池singleton_pool和可用于标准库的pool_alloc.

pool:
 pool是最简单也最容易使用的内存池类,可以返回一个简单数据类型的内存指针。
它位于名字空间boost,为了使用pool组建,需要包含头文件<boost/pool/pool.hpp>,即
#include<boost/pool/pool.hpp>
using namestd boost;

操作函数:
 pool的模板类型参数UserAllocator是一个用户定义的内存分配器,它实现了特定的内存分配算法,通常可以直接用默认的default_user_allocator_new_delete.
 pool的构造函数接受一个size_type类型的整数requested_size,指示每次pool分配内存块的大小(而不是pool内存池的大小),这个值可以用get_requested_size()获得。pool会根据需要自动地向系统申请或归还使用的内存,在析构时,pool将自动释放它所持有的所有内存块。
 成员函数malloc()和ordered_malloc()行为很类似c中的全局函数malloc(),用void*指针返回从内存池中分配的内存块,大小为构造函数中指定的requested_size,如果内存分配失败,函数会返回0,不会抛出异常。malloc()从内存池中任意分配一个内存块,而ordered_malloc()则在分配的同时合并空闲块链表。ordered_malloc()带参数的形式还可以连续分配n块的内存,分配后的内存块可以用is_from()函数测试是否是从这个内存池分配出去的。
 与malloc()对应的一组函数是free(),用来手工释放之前分配的内存块,这些内存块必须是从这个内存池分配出去的(is_from(chunk) == true).一般情况内存池会自动管理内存分配,不应该调用free()函数,除非你认为内存池的空间已经不足,必须释放已经分配的内存.
 release_memory()让内存池释放所有未被分配的内存,但已分配的内存块不受影响,purge_memory()则强制释放pool持有的所有内存,不管内存块是否被使用,实际上,pool的析构函数就调用了purge_memory().这两个函数一般情况下也不应该由程序员手工调用.

用法:
 pool可以像c中的malloc()一样分配内存,除非有特殊要求,否则不必对分配的内存调用free()释放,pool会很好地管理内存.
例:
#include <boost/pool/pool.hpp>
using namespace boost;
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
 pool<> pl(sizeof(int));//一个可分配int的内存池

 int *p = (int*)pl.malloc();//必须把void*转换成需要的类型
 cout<<"is_from pool "<<pl.is_from(p)<<endl;
 pl.free(p);

 for (int iLoop = 0; iLoop < 100; ++iLoop)
  pl.ordered_malloc(10);
 return 0;
}//内存池对象析构,所有分配的内存在这里都被释放
 因为pool在分配内存失败的时候不会抛出异常,所以实际编写的代码应该检查malloc()函数返回的指针,以防止空指针错误。
 注意:pool<>只能作为普通数据类型如int,double等内存池,不能应用于复杂的类和对象,因为它只分配内存,不调用构造函数,这个时候需要用object_pool.


object_pool:
 object_pool是用于类实例(对象)的内存池,它的功能与pool类似,但会在析构时对所有已经分配的内存块调用析构函数,从而正确的释放资源.

操作函数:
 object_pool是pool的子类,但它使用的是保护继承,因此不能使用pool的接口.
 object_pool的模板类型参数ElementType指定了object_pool要分配的元素类型,要求其析构函数不能抛出异常。一旦在模板中指定了类型,object_pool实例就不能再用于分配其他类型的对象。
 malloc()和free()函数分别分配和释放一块类型为ElementType*的内存块,同样,可以用is_from()来测试内存块的归属,只有是本内存池分配的内存才能被free()释放。但它们被调用时并不调用类的构造函数和析构函数,也就是说操作的是一块原始内存块,里面的值是未定义的,因此应当尽量少使用malloc()和free().
 object_pool的特殊之处是construct()和destory()函数,construct()实际上是一组函数,有多个参数的重载形式,它先调用malloc()分配内存,然后再在内存块上使用传入的参数调用类的构造函数,返回的是一个已经初始化的对象指针。destory()则先调用对象的析构函数,然后再用free()释放内存块。
 这些函数都不会抛出异常,如果内存分配失败,将返回0.

用法:
 object_pool的用法也是很简单,既可以像pool那样分配原始内存块,也可以使用construct()来直接在内存池中创建对象。
代码示范:
#include <boost/pool/object_pool.hpp>
using namespace boost;
using namespace std;
struct demo_class
{
public:
 int a,b,c;
 demo_class(int x = 1, int y = 2, int z = 3):a(x), b(y), c(z)
 {
 }
};

int _tmain(int argc, _TCHAR* argv[])
{
 object_pool<demo_class> pl;//对象内存池
 demo_class *p = pl.malloc();
 assert(pl.is_from(p));
 //p指向的内存未经过初始化
 assert(p->a != 1 || p->b != 2 || p->c != 3);
 p = pl.construct(7, 8, 9);//构造一个对象,可以传递参数
 assert(p->a == 7);
 object_pool<string> pls;//定义一个分配string对象的内存池
 for (int iLoop = 0; iLoop < 10; ++iLoop)//连续分配大量string对象
 {
  string *ps = pls.construct("hello object_pool");
  cout << *ps<<endl;
 }
 return 0;
}//所有创建的对象在这里都被正确析构,释放内存.

使用更多的构造参数:
 默认情况下,在使用object_pool的construct()的时候只能最多使用3个参数来创建对象,但有时候可能会定义3个以上参数的构造函数,此时construct()的默认重载形式就不能用了。
 但construct()被设计是可以扩展的,它基于宏预处理m4(通常Unix和Linux系统自带,也有Windows的版本)实现了一个扩展机制,可以自动生成接受任意数量参数的construct()函数。
 pool库在目录/boost/pool/detail下提供了一个名为pool_construct.m4和pool_construct_simple.m4的脚本,并同时提供可在Unix/Linux和Windows下运行的同名sh和bat可执行脚本文件,只需要简单地向批处理脚本传递一个整数的参数N,m4就会自动生成能够创建具有N个参数的construct()函数源代码。
例如,在Linux,执行命令:
./pool_construct_simple.sh 5;./pool_construct.sh 5
将生成两个同名的.inc文件,里面包含了新的construct()函数定义,能够支持最多传递5个参数创建对象,由于m4生成的是c++源代码,因此.inc文件也可以拷贝到其他操作系统的Boost库中使用。

 扩展construct()函数时请慎重,数量过多的参数定义会导致程序的编译时间增加。
 如果只是临时的需要增加construct()函数的参数数量,或者工作的系统上m4不可用,可以简单地定义一个辅助模板函数。
 下面的代码模仿construct()函数实现了一个可接受4个参数的创建函数:

template<typename P, typename T0, typename T1, typename T2, typename T3>
inline typename P::element_type*
construct(P& p, const T0& a0, const T1& a1, const T2& a2, const T3& a3)
{
 typename P::element_type* mem = p.malloc(); 
 assert(mem !=0 );
 new (mem) p::element_type(a0, a1, a2, a3);
 return mem;
}
自由函数construct()接受5个参数,第一个是object_pool对象,其后是创建对象所需的4个参数,要创建的对象类型可以使用object_pool的内部类型定义element_type来获得。函数中首先调用malloc()分配一块内存,然后调用不太常见的"定位new表达式"(placement new expression)创建对象。
假设有如下一个4参数构造函数的类和一个object_pool对象
struct demo_class
{
 demo_class(int, int, int, int){}
 ~demo_class(){}
};
object_pool<demo_class> pl;
那么使用m4和自定义的construct()创建对象的代码就是:
demo_class* d1 = pl.construct(1, 2, 3, 4);
demo_class* d2 = construct(pl, 1, 2, 3, 4);

 
 
singleton_pool:
 singleton_pool与pool的接口完全一致,可以分配简单数据类型(POD)的内存指针,但它是一个单件,并提供线程安全。

用法:
 singleton_pool主要有两个模板类型参数(其余的可以使用缺省值).第一个Tag仅仅是用于标记不同的单件,可以使空类,甚至是声明(这个用法还被用于boost.exception.),第二个参数RequestedSize等同于pool构造函数中的整数requested_size,指示pool分配内存块的大小。

 singleton_pool的接口与pool完全一致,但成员函数均是静态的,因此不需要声明实例,直接用域操作符::来调用静态成员函数,因为singleton_pool是单件,所以它的生命周期与整个程序同样长,除非手动调用release_memory()或purge_memory(),否则singleton_pool不会自动释放所占用的内存。
代码示范:

#include <boost/pool/singleton_pool.hpp>
using namespace boost;
using namespace std;
struct pool_tag{};         //仅仅用于标记的空类
typedef singleton_pool<pool_tag, sizeof(int)> spl; //内存池定义
int _tmain(int argc, _TCHAR* argv[])
{
 int *p = (int*)spl::malloc();     //分配一个整数内存块
 assert(spl::is_from(p));
 spl::release_memory();       //释放所有未被分配的内存 
 return 0;
}//spl的内存直到程序结束才完全释放,而不是退出作用域


pool_alloc:
 pool_alloc提供了两个可以用于标准容器模板参数的内存分配器,分别是pool_alloc和fast_pool_allocator,它们的行为与之前的内存池类有一点不同---当内存分配失败时会抛出异常std::bad_alloc.
 除非有特殊的需求,我们应该总使用STL实现自带的内存分配器,使用pool_alloc需要经过仔细的测试,以保证它与容器可以共同工作.
代码示例:
#include <boost/pool/pool_alloc.hpp>
using namespace boost;
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
 //使用pool_allocator代替标准容器默认的内存分配器
 vector<int, pool_allocator<int>> v;
 v.push_back(10);//vector将使用新的分配器良好工作
 cout<<v.size();
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值