一、概述
boost.pool库基于简单分隔存储思想实现了一个快速、紧凑的内存池库,不仅能够管理大量的对象,还可以被用作STL的内存分配器。某种程度上讲,它近似于一个小型的垃圾回收机制,在需要大量分配/释放小对象时很有效率,而且完全不需要考虑delete。
pool库包含四个组成部分:最简单的pool、分配类实例的object_pool、单例内存池singleton_pool和可用于标准库的pool_alloc;
二、各组件介绍
1、pool
返回一个简单数据类型(POD: Plain Old Data : 普通旧式数据)的内存指针。位于名字空间boost,为了使用pool组件,需要包含头文件<boost/pool/pool.hpp>。
用法:
例子:
pool<> p1( sizeof(int) );//一个可分配int的内存池
int *p = static_cast< int* >( p1.malloc() );//必须把void* 转换成需要的类型
if( p == NULL )//pool在分配内存失败的时候不会抛出异常,所以实际编写的代码应该检查malloc()函数返回的指针,以防止空指针错误。
{
return 0;
}
assert( p1.is_from(p) );
p1.free( p );
for( int i=0; i<100; ++i)
{
p1.ordered_malloc( 10 );
}
return 0;
注意两点:
1*、因为pool在分配内存失败时不会抛出异常,所以实际编写的代码应该检查malloc()函数返回的指针,以防止空指针错误;
2*、pool只能作为普通数据类型如int、double等的内存池,不能应用于复杂的类和对象,因为它只分配内存,不调用构造函数。
问题:实际测试时发现,执行上述要依赖四个static库:chrono、date_time、system、thread;如果你的boost编译正确,可以在bin.v2/libs下找到相应的库。
2、object_pool
object_pool是用于类实例(对象)的内存池,它的功能与pool类似,但会在析构时对所有已经分配的内存块调用析构函数,从而正确地释放资源。
object_pool位于名字空间boost,为了使用object_pool组件,需要包含头文件<boost/pool/object_pool.hpp>。
object_pool是pool的子类,但它使用的是保护继承,因此不能使用pool的接口,但基本操作还是很相似的。
使用:
malloc()和free()函数分别分配和释放一块类型为ElementType* 的内存块,同样,可以使用is_from()来测试内存块的归属,只有是本地内存池分配的内存才能被free()释放。值得注意的是,它们被调用时并不调用类的构造函数和析构函数,也就是说,操作的是一块原始内存块,里面的值是未定义的,因此我们应当尽量少使用malloc()和free()。
object_pool的真正价值在于construct()和destroy()函数。construct()实际上是一组函数,有多个参数的重载形式(目前最多支持3个参数,但可以扩展),它先调用malloc()分配内存,然后再在内存块上使用传入的参数调用类的构造函数,返回的是一个已经初始化的对象指针。destory()则先调用对象的析构函数,然后再用free()释放内存块。
这些函数都不会抛出异常,如果内存分配失败,将返回0;
用法:
object_pool< DemoClass > p1; //对象内存池
DemoClass *p = p1.malloc(); //分配一个原始内存块
assert( p1.is_from(p) );
assert( p->a !=1 || p->b != 2 || p->c != 3);//p指向的内存未经过初始化
p = p1.construct(1,2,3);//构造一个对象,可以传递参数
assert( p->a == 1);
object_pool<std::string> pls;//定义一个分配string对象的内存池
for( int i=0; i<10; ++i)//连续分配大量的string对象
{
std::string *ps = pls.construct("hello object_pool");
std::cout<< *ps <<std::endl;
}
//所有创建的对象在这里都被正确析构,释放内存
扩展构造参数:
扩展construct()函数时请慎重,数量过多的参数定义会导致程序的编译时间增加,所以最好只定义最合适需要的参数数量;如果只是临时的需要增加construct()函数的参数数量,可以定义一个辅助模板函数。
例如,模仿construct()函数实现可接受4个参数的创建函数:
//注:参数一 P :是object_pool对象
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);//使用“定位new表达式”(placement new expression)
return mem;
}
使用上述模板:
object_pool<DemoClass> pOP;
DemoClass* pTest = construct( pOP, 1, 2, 3, 4);
3、singleton_pool
singleton_pool与pool的接口完全一致,可以分配简单数据类型(POD)的内存指针,但它是一个单例,并提供线程安全;
其内部实现了一个较简单的、泛型的单例类,保证在main()函数运行之前就创建单例。
singleton_pool位于名字空间boost,为了使用singleton_pool组件,需要包含头文件<boost/pool/singleton_pool.hpp>
注意:singleton_pool的成员函数都是静态的,因此不需要声明singleton_pool的实例,因为singleton_pool是单例,所以它的生命周期与整个程序同样长,除非手动调用release_memory()或purge_momory(), 否则singleton_pool不会自动释放所占用的内存。
用例:
struct pool_tag{};//仅仅用于标记的空类
typedef singleton_pool< pool_tag, sizeof(int) > sp1;//内存池定义
int *pInt = (int*) sp1::malloc();
assert( sp1::is_from(pInt) );
sp1::release_memory();//释放所有未被分配的内存,sp1的内存直到程序结束才完全释放,而不是退出作用域
4、pool_alloc
pool_alloc提供了两个可以用于标准容器模板参数的内存分配器,分别是pool_alloc和fast_pool_allocator,它们的行为与之前的内存池类有一点不同——当内存分配失败时会抛出异常std::bad_alloc。
除非有特别的要求,我们应该总使用STL实现自带的内存分配器,使用pool_alloc需要经过仔细的测试,以保证它与容器可以共同工作。
它们位于名字空间boost,需要包含头文件<boost/pool_alloc.hpp>
示例用法:
std::vector< int, pool_allocator<int> > v;//使用pool_allocator代替标准容器默认的内存分配器,
//vector将使用新的内存分配器良好工作
v.push_back( 10 );
std::cout<< v.size() <<std::endl;