在上篇博文中,我們系統地學習了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,有時多多嘗試其他的方法,說不定能夠找到更好地解決方案,好了,這篇博文到此結束,多謝了。
如果需要,請註明轉載,多謝