Boost之內存管理學習(二)

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

如果需要,請註明轉載,多謝
                   

發佈了52 篇原創文章 · 獲贊 10 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章