pool

boost.pool

boost.pool库基于简单分隔存储的思想实现了一个快速、紧凑的内存池
不仅能够管理大量对象,还可以用作STL的内存分配器,在需要大量分配或释放小对象时效率很高,且不需要考虑delete

包含4个组成部分

  • 简单pool
  • 分配类实例object_pool
  • 单件内存池singleton_pool
  • 用于标准库pool_alloc

pool

最简单和最容易使用的内存池类,返回一个简单数据类型POD的内存指针,位于命名空间boost

#include<boost/pool/pool.hpp>
using namespace boost;

类原型

template<typename UserAllocator=default_user_allocator_new_delete>
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();
};

模板类型参数UserAllocator使用默认的内存分配器,基于new[]delete[]实现

malloc()ordered_malloc()从内存池中分配内存块,大小由构造函数参数requested_size指定,若分配失败会返回空指针,不抛出异常

malloc()从内存池中任意分配一个内存块,而ordered_malloc()在分配的同时合并空闲块链表,带参数的ordered_malloc()可以连续分配n块内存

is_from()测试内存块是否属于当前内存池

一般情况下,内存池会自动管理内存分配,不需要调用free(),除非用户认为内存池空间不足,必须先释放一定的内存

release_memory()释放内存池中所有未被分配的内存,已分配的不受影响
purge_memory()强制释放内存池持有的所有内存,析构函数就是调用的该函数

示例如下:

int main(){
    pool<> p(sizeof(int)); // 一个可分配int的内存池
    int* p1= static_cast<int*>(p.malloc()); // 转换void*
    if(p1!=nullptr){
    }
    assert(p1.is_from(p));

    p.free(p1);
    for(int i=0; i<100; ++i){ // 连续分配大量的内存
        p.ordered_malloc(10);
    }
}

pool只能用于普通数据类型的内存池,不能应用于复杂类型和对象,因为其只分配内存,不构造对象

object_pool

应用于类实例的内存池,功能与pool类似,但其会在析构时对所有已分配的内存块调用析构函数

#include<boost/pool/object_pool.hpp>
using namespace boost;

类原型

template<typename T, typename UserAllocator>
class object_pool:protected pool<UserAllocator>{
public:
    typedef T element_type;
public:
    object_pool();
    ~object_pool();

    element_type* malloc();
    void free(element_type* p);
    bool is_from(element_type* p)const;

    element_type* construct(...);
    void destroy(element_type* p);
};

object_pool的模板类型参数T指定要分配的元素类型,要求其析构函数不抛出异常
一旦指定了类型,object_pool对象就不能再用于分配其他类型

construct()有多个重载形式,先调用malloc()分配内存,再根据参数调用类的构造函数
destroy()先调用对象的析构函数,再用free()释放内存块

malloc()free()表示分配和释放一块类型为T*的内存块
只有在当前内存池中分配的内存块才能被free()释放
这两个函数被调用时不调用类构造函数和析构函数,操作的是原始内存块,里面的值是未定义的

示例如下:

struct demo{
public:
    int a,b,c;
    demo(int x=1, int y=2, int z=3):a(x),b(y),c(z){}
};

int main(){
    object_pool<demo> p;
    auto p1= p.malloc();
    assert(p.is_from(p1));

    // p1指向的内存未初始化
    assert(p->a!=1 || p->b!=2 || p->c!=3);

    p1= p.construct(7,8,9);
    assert(p->a==7);

    object_pool<string> pstr;
    for(int i=0; i<10; ++i){
        string* p2= pstr.construct("hello object_pool");
        std::cout<<*p2<<std::endl;
    }
}

默认情况下,使用object_pool只能接收3个参数来创建对象
使用C++标准的可变参数模板解决该问题

template<typename T, typename ... Args>
inline typename T::element_type* construct(T& t, Args&& ... args){
    typename T::element_type* mem= t.malloc();
    assert(mem!=0);

    new (mem) typename T::element_type(std::forward<Args>(args)...); // 完美转发
    return mem;
}

// 使用
object_pool<demo> p;
auto p1= construct(p, 1,2,3,4);

singleton_pool

与pool类似,用于分配POD,但是该类对象是一个单件

#include<boost/pool/singleton_pool.hpp>
using namespace boost;

默认使用boost.thread库提供线程安全,所以需要将其链接boost_thread库
若不使用多线程,可在头文件前定义BOOST_POOL_NO_MT

类原型

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();
};

模板类型参数Tag用于标记不同的单件,可以是空类,甚至可以只进行声明
参数RequestedSize等同于pool中的requested_size,用来指示内存块大小

因为singleton_pool是单件,所有其声明周期与整个程序一致,除非手动释放

示例如下:

struct pool_tag(); // 仅用于标记的空类
typedef singleton_pool<pool_tag, sizeof(int)> spl; // 内存池定义

int main(){
    int* p= (int*)spl::malloc();
    assert(spl::is_from(p))
    spl::release_memory();
}
posted @ 2024-11-01 19:59  sgqmax  阅读(2)  评论(0编辑  收藏  举报