现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

Boost之内存管理学习(二)

2019-12-22 18:05 工业·编程 ⁄ 共 6597字 ⁄ 字号 暂无评论

上篇博文中,我们系统地学习了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

给我留言

留言无头像?