在上篇博文中,我们系统地学习了Boost库中智能指针的学习,在处理单个对象时,采用智能指针是一个很不错的选择,但是当需要管理的对象不止一个时,这时候智能指针就有点无能为力了,今天我们就来看看Boost库中pool库的一些东西,Boost.pool库是基于简单分隔存储思想实现的一个快速、紧凑的内存池库,不仅能够管理大量的对象,而且还可以作为stl的内存分配器,在需要大量地分配和释放小对象时,很有效率,且完全不用考虑delete,在pool库中主要包括了四个部分:最单纯的POOL,分配类实例的OBJECT_POOL,单件内存池SINGLETON_POOL以及可以用标准库的POOL_ALLOC,下面我们就一起来看看这四种内存池的一些基本用法吧。
1)最单纯的POOL
这种POOL是四种内存池库中最简单的一种,其仅支持简单的数据类型(POD),其返回类型也是简单的POD指针,其类的声明如下所示:
template<typename UserAllocator=...>
class pool
{
public:
explicit pool(size_type requested_size);
~pool();
size_type get_requested_size()const;
void* malloc();
void* ordered_malloc();
void* ordered_malloc(size_type n);
bool is_from(void* chunk) const;
void free(void* chunk);
void ordered_free(void* chunk);
void free(void* chunk,size_type n);
void ordered_free(void* chunk,size_type n);
bool release_memory();
bool purge_memory();
...
};
通过上面的类声明中,我们需要知道:
1)pool的模板类型参数UserAllocator是个用户定义的内存分配器,并且在一般的使用时,都是采用默认的default_user_allocator_new_delete
2)构造函数中的requested_size代表意思:每次从pool中分配的内存块大小,而非pool内存池的大小
3)分配内存时,主要是通过malloc和ordered_mallo函数,malloc是从内存池中任意分配一块内存,而ordered_malloc则是在分配的时候同时合并空闲链表,而带了参数的ordered_malloc则是;连续分配n块内存
4)从内存池中分配出去的内存块,一般不需要用户来释放,但是一些个别的案例会有这个要求,在释放内存池时也分为了两种情形,分别对应着两个函数:release_memory和purge_memory,这两个函数的区别在于:release_memory释放未被分配的内存,已分配的内存不受影响,而purge_memory函数则是强制释放所有的内存,不管是否分配,一般在pool析构中会调用purge_memory。
接下来,我们来看看一些简单的测试用例吧,熟悉下pool的基本用法,代码如下:
#include <boost/pool/pool.hpp>
#include <iostream>
using namespace boost;
using namespace std;
int main()
{
pool<> p1(sizeof(int));
int* p = (int*)p1.malloc();
assert(p1.is_from(p));
p1.free(p);
int* p2 = (int*)p1.ordered_malloc(10);
for(int i = 0;i<10;i++)
p2[i] = i;
for(int i = 0;i<10;i++)
cout<<p2[i]<<endl;
return 0;
}
测试结果:
0
1
2
3
4
5
6
7
8
9
在这里,我们需要在强调一点:pool类型的内存池只能使用在一些POD类型的数据上,例如:一些基础类型,而不能使用在一些复杂的类型上,因为pool内存池只是负责分配内存,而不会调用构造函数,如果需要构造复杂类型的内存池,我们需要使用另外一个内存池:object_pool。
2. OBJECT_POOL 内存池
OBJECT_POOL池与POOL池类似,主要的不同就是它是用于类实例的内存池,并且在析构时会调用内存块中的对象析构函数来释放相应资源,首先来看看类声明吧,代码如下:
template<typename ElementType>
class object_pool : protected pool
{
public:
object_pool();
~object_pool();
element_type* malloc();
void free(element_type* p);
bool is_free(element_type* p) const;
element_type* contruct(...);
void destory(element_type* p);
...
};
在使用object_pool池时,我们需要注意一下几点:
1)由于object_pool是pool的子类,并且其使用了保护类型,所以object_pool不能使用pool类的接口
2)在object_pool的模板参数中,指明了内存池构造对象的类型,所以一旦指定了类型,object_pool就不能构造其他类型的对象
3)在object_pool构造对象时,首先会通过malloc来分配相应的内存,然后会调用传进来的类的构造函数,这个过程在contruct实现
4)所以上述函数在出现异常时,将返回0,而不会抛出异常。
下面我们就来看看几个测试用例,来熟悉下object_pool的一些基础用法,代码如下:
#include <boost/pool/object_pool.hpp>
#include <iostream>
#include <stdio.h>
using namespace boost;
using namespace std;
class Book
{
public:
Book(std::string bookname = std::string(),const int price = 0):bookname(bookname),price(price)
{
printf("Contruct\n");
}
~Book()
{
printf("DesContruct\n");
}
void setBookName(std::string bookname)
{
this->bookname = bookname;
}
void setBookPrice(const int price)
{
this->price = price;
}
void display()
{
cout<<"bookname:"<<bookname<<" "<<"price:"<<price<<endl;
}
private:
std::string bookname;
int price;
};
int main()
{
object_pool<Book> p1;
Book* book = p1.construct("",0);
book->setBookName("C++ Design");
book->setBookPrice(20);
book->display();
Book* boo = p1.construct("C",10);
boo->display();
return 0 ;
}
测试结果:
Contruct
bookname:C++ Design price:20
Contruct
bookname:C price:10
DesContruct
DesContruct
在默认情况下,construct函数只接受少于3个参数的情形,如果定义构造函数的参数多于3个时,这时我们需要对construct进行扩展了,这种扩展的方式基于宏预处理m4实现的一种扩展机制,可以自动生成接受任意数量参数的construct函数,下面我们就来实现一个接受四个参数的创建函数,代码如下:
#include <boost/pool/object_pool.hpp>
#include <iostream>
#include <stdio.h>
using namespace boost;
using namespace std;
class Book
{
public:
Book(const std::string bookname = std::string(),const std::string bookid = std::string(),const std::string
const int price = 0):bookname(bookname),bookid(bookid),publish(publish),price(price)
{
printf("Contruct\n");
}
~Book()
{
printf("DesContruct\n");
}
void setBookName(const std::string bookname)
{
this->bookname = bookname;
}
void setBookPrice(const int price)
{
this->price = price;
}
void setBookId(const std::string bookid)
{
this->bookid = bookid;
}
void setPublish(const std::string publish)
{
this->publish = publish;
}
void display()
{
cout<<"bookname:"<<bookname<<" "<<"price:"<<price<<" "<<"bookid:"<<bookid<<" "<<"publish:"<<publish<<e
}
private:
std::string bookname;
std::string bookid;
std::string publish;
int price;
};
template<typename P,typename T0,typename T1,typename T2,typename T3>
inline typename P::element_type* construct(P& p,const T0& t0,const T1& t1,const T2& t2,const T3& t3)
{
typename P::element_type* mem= p.malloc();
assert(!mem);
new(mem)P::element_type(t0,t1,t2,t3);
return mem;
}
3. SINGLETON_POOL
singleton_pool与pool提供的结构式完全相同的,可以分配简单POD类型内存指针,只是它是一个单件,并且能够提供线程安全,下面我们就来看看singleton_pool的类声明吧,代码如下:
template<typename tag,unsigned RequestedSize>
class singleton_pool
{
public:
static bool is_from(void* ptr);
static void* malloc();
static void* ordered_malloc();
static void* ordered_malloc(size_type n);
static void free(void* ptr);
static void ordered_free(void* ptr);
static void free(void* ptr,std::size_t n);
static void ordered_free(void* ptr,size_type n);
static bool release_memory();
static bool purge_memory();
...
};
从上面的类声明中可以看出,singleton_pool类的所有的成员函数都是static,因此,在使用single_pool时,我们只需要使用single_pool::的形式即可,下面就来看看几个简单的测试用例吧,熟悉下singleton_pool的使用方法,代码如下:
#include <boost/pool/singleton_pool.hpp>
#include <stdio.h>
using namespace boost;
using namespace std;
struct pool_tag
{
};
typedef singleton_pool<pool_tag,sizeof(int)> sp;
int main()
{
int* p = (int*)sp::malloc();
*p = 100;
printf("*p=%d\n",*p);
assert(sp::is_from(p));
sp::release_memory();
return 0;
}
4. POOL_ALLOC
这个库一般使用的比较少,它提供了两个用于标准容器模板参数的内存分配器,一个是pool_alloc,一个是fast_pool_allocator,它们的行为与之前所介绍的内存池有一点不同:当内存分配失败时,其会抛出std::bad_alloc异常,类声明文件从略,下面我们来看看几个简单的测试用例吧,代码如下:
#include <boost/pool/pool_alloc.hpp>
#include <vector>
using namespace boost;
using namespace std;
int main()
{
vector<int,pool_allocator<int> >v;
v.push_back(1000);
cout<<v.size()<<endl;
return 0;
}
总结
本篇博文主要是分析了boost库中几个pool库,并且针对每个库进行了简单的学习,其实这些库在实际的开发中用的比较广泛,有些东西其实是需要不断地实践和总结,才能真正地做到精通,这篇博文就算是一个开端吧,自己会在今后的开发中多尝试使用一些自己学习到的东西,不要一味地使用stl,有时多多尝试其他的方法,说不定能够找到更好地解决方案。
作者:zmyer