Boost练习4——内存管理之内存池

一、概述

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;

 

 

 

 

 


 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值