Boost库学习(3) 内存管理
preface:
1、传统C++内存管理中可能出现的问题:内存泄露、野指针、访问越界;相关解决方案:智能指针 std::auto_ptr,不能完全解决内存管理中出现的问题;
2、关于smart_ptr库的概述:
2.1、C++程序员通常采用RAII(资源获取即初始化)机制管理内存资源,在使用资源的类的构造函数中申请资源,最后在析构函数中释放资源;如果对象的声明方式是在栈上,RAII机制可以工作正常,但是如果对象是用new操作符在堆上创建的,那么它的析构函数不会自动的调用,需要手动delete,然而即便是C++熟手,也很容易忘记;
2.2、智能指针实践了代理模式,代理了原始的“裸”指针行为,为它添加了许多有用的特性;
2.3、auto_ptr很好用,但是没有覆盖智能指针的全部领域,尤其是引用计数型的智能指针;boost.smart_ptr库是对C++98的一个绝佳补充,它提供了六种智能指针:scoped_ptr、scoped_array、shared_ptr、shared_array、weak_ptr、intrusive_ptr,库中的两个类shared_ptr和weak_ptr已经加入到C++的新标准中TR1库中。
2.4、这些智能指针都位于名字空间boost,为了使用smart_ptr组件,需要包含头文件:
#include"boost\smart_ptr.hpp"
using namespace boost;
一、scoped_ptr
1、简介:类似于auto_ptr,包装了new在堆上分配的动态对象,保证动态创建的对象在任何时候都可以正确的被删除,但是scoped_ptr的所有权更加严格,不能转让,一旦scoped_ptr取得对象的管理权,就无法从它哪里取回来了;
2、类摘要:
1 template<class T> 2 class scoped_ptr 3 { 4 private: 5 T* px; 6 scoped_ptr(scoped_ptr const &); 7 scoped_ptr& operator=(scoped_ptr const&); 8 public: 9 explicit scoped_ptr(T* p = 0); 10 ~scoped_ptr(); 11 void reset(T* p=0); 12 13 T& operator*() const; 14 T* operator->() const; 15 T* get() const; 16 17 operator unspecified-bool-type() const; 18 void swap(scoped_ptr &b); 19 };
说明:<a>拷贝构造函数和赋值函数都是私有,保证其不被复制,保证了被他管理的指针的所有权不被转让;
<b>scoped_ptr不支持比较的操作,它已经将operator==和operator!=两个操作符号的重载声明为私有了;但是其提供了可以再bool语境中自动转换成bool值的功能,用来测试scoped_ptr是否持有一个有效的指针(非空),它可以代替和空指针的比较操作。
3、用法:在原本使用指针变量接收new表达式结果的地方改成用scoped_ptr对象,然后去掉那些多余的try/catch和delete操作就可以了。
备注:与auto_ptr的区别和联系
scoped_ptr的用法和auto的几乎一样,大多数的情况下,可以和auto_ptr互换,也可以从auto_ptr获取指针的管理权(同时auto_ptr失去管理权);scoped_ptr也具有auto_ptr同样的缺陷---不能用作容器元素,但是原因不同,auto_ptr是因为它的转移语意,而scoped_ptr则是因为不支持拷贝和赋值,不符号容器对元素的要求。scoped_ptr与auto_ptr根本性的区别在于指针的所有权,auto_ptr特意被设计为指针的所有权是可转移的,可以在函数之间传递,而同一时刻只能有一个auto_ptr管理指针,而scoped_ptr把拷贝构造函数和赋值函数都声明为私有,拒绝了指针所有权的转让,除了scoped_ptr自己,其他任何人都无权访问被管理的指针,从而保证了指针的绝对安全。
二、scoped_array
1、简介:包装了new[]操作符在堆上分配的动态数组,为动态数组提供了代理,保证可以正确释放内存。scoped_array弥补了标准库中没有指向数组的智能指针的缺憾。
2、类摘要:
1 template<class T>
2 class scoped_array
3 {
4 public:
5 explicit scoped_array(T* p=0);
6 ~scoped_array();
7
8 void reset(T* p = 0);
9 T& operator[](std::ptrdiff_t i) const;
10 T* get() const;
11
12 operator unspecifid-bool-type() const;
13 void swap(scoped_array& b);
14 };
备注:
<a>构造函数接受的指针p必须是new[]的结果
<b>没有*,->的操作符重载,因为scoped_array持有的不是一个普通的指针;
<c>析构函数使用delete[]释放资源,而不是delete;
<d>提供operator[]操作符重载,可以像普通数组一样用下标访问元素;
<e>没有begin(),end()等类似容器的迭代器操作的函数;
3、用法:
3.1、scoped_array重载了operator[],因此它用起来就像是一个普通的数组,但是它不提供指针运算,所以不能用“数组首地址+N”的方式访问数组元素;
3.2、使用重载的operaor[]时要小心,scoped_array不提供数组索引范围检查;
使用建议:
scoped_array的功能很限,不能动态增长,也没有迭代器支持,不能搭配STL算法,仅有一个“裸”数组接口。在需要动态数组的情况下,我们应该使用std::vector,它比scoped_array提供了更多了灵活性。
三、shared_ptr
1、简介:是boost.smart_ptr库中最有价值、最重要的组成部分,也是最有用的。shared_ptr和scoped_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针,可以自由的拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它才删除动态分配的对象,share_ptr也可以安全的放到标准容器中,
2、类摘要:
1 template<class T>
2 class shared_ptr
3 {
4 public:
5 typedef T element_type;
6
7 shared_ptr();
8 template<class Y> explicit shared_ptr(Y* p);
9 template<class Y,class D>shared_ptr(Y*p,D d);
10 ~shared_ptr();
11
12 shared_ptr(shared_ptr const& r);
13 template<class Y> explicit shared_ptr(std::auto_ptr<Y>& r);
14
15 shared_ptr& operator=(shared_ptr const& r);
16 template<class Y> shared_ptr& operator=(shared_ptr<Y> const& r);
17 template<class Y> shared_ptr& operator=(std::auto_ptr<Y> const& r);
18
19 void reset();
20 template<class Y> void reset(Y* p);
21 template<class Y,class D>void rest(Y* p,D d);
22
23 T& operator*() const;
24 T* operator->() const;
25 T* get() const;
26
27 bool unique() const;
28 long use_count() const;
29
30 operator unspecified-bool-type() const;
31 void swap(shared_ptr& b);
32 };
备注:shared_ptr提供多种形式的构造函数
<a>无参share_ptr()创建一个持有空指针的shared_ptr;
<b>shared_ptr(Y* p)获得指向类型T的指针p的管理权,同时引用计数置1,这个构造要求类型必须能够转化为T类型;
<c>shared_ptr(std::auto_ptr<Y>& r)从一个auto_ptr获得指针的管理权,引用计数置1,同时auto_ptr自动失去管理权;
<d>operator=赋值符可以从另外一个shared_ptr或auto_ptr获得指针管理权,其行为同构造函数;
<e>shared_ptr(Y* p,D d)行为类似于shared_ptr(Y* p),但使用参数d指定了析构时的定制删除器,而不是简单的delete。
对于引用计数的问题:
<a>shared_ptr的reset函数与scoped_ptr的不尽相同,它的作用是将引用计数减1,停止对指针的共享,除非引用计数为0,否则不会发生删除操作。带参数的reset()则类似相同形式的构造函数,原指针引用计数减1的同时改为管理另一指针。
<b>shared_ptr有两个函数来检查引用计数,unique()在shared_ptr是指针的唯一所有者时返回true,use_count()返回当前指针的引用计数。
关于“比较”运算
<a>shared_ptr支持比较运算,可以测试两个shared_ptr的相等或不相等,比较基于内部保存的指针,相当于a.get()==b.get()。shared_ptr还可以用operator<比较大小,同样基于内部保存的指针,但不提供除operator<意外的比较操作符,这使得shared_ptr可以用于标准关联容器(set和map);
关于“类型转换”
在编写基于虚函数的多态代码时指针的类型转换很有用,但对于shared_ptr不能使用static_case<T*>(p.get())的形式,这将导致转型后的指针无法再不被shared_ptr正确管理,为了支持这样的用法,shared_ptr提供了类似的转型函数:static_pointer_case<T>()、const_pointer_case<T>()和dynamic_pointer_case<T>(),他们与标准的static_case<T>、const_case<T>和dynamic_case<T>类似,但返回的是转型后的shared_ptr.
3、用法
3.1 工厂函数(消除new和delete出现的不对称性)
1 #include "stdafx.h"
2 #include<iostream>
3 #include<map>
4 #include<memory>
5 #include<vector>
6 #include<cassert>
7 using namespace std;
8
9 #include "boost\smart_ptr.hpp"
10 #include "boost\make_shared.hpp"
11
12 int _tmain(int argc, _TCHAR* argv[])
13 {
14 shared_ptr<string> sp = make_shared<string>("make_shared");
15 shared_ptr< vector<int> > spv = make_shared< vector<int> >(10,2);
16 assert(spv->size() == 10);
17 return 0;
18 }
说明:
<a>shared_ptr在头文件<boost/make_shared.hpp>中提供了一个自由工厂函数,make_shared<T>来消除显示new的调用,make_shared的函数可以接受最多10个参数,然后把它们传递给类型T的构造函数,创建一个shared_ptr<T>对象然后返回。make_shared函数要比直接创建shared_ptr对象快且高效,内部仅分配一次内存,来消除shared_ptr构造开销;
3.2 应用于标准容器
1 #include "stdafx.h"
2 #include<iostream>
3 #include<map>
4 #include<memory>
5 #include<vector>
6 #include<cassert>
7 using namespace std;
8
9 #include "boost\smart_ptr.hpp"
10 #include "boost\make_shared.hpp"
11
12 int _tmain(int argc, _TCHAR* argv[])
13 {
14 typedef vector< shared_ptr<int> > vs;
15 vs v(10);
16
17 int i = 0;
18 for(vs::iterator pos =v.begin();pos != v.end();++pos)
19 {
20 (*pos) = make_shared<int>(++i);
21 cout<<*(*pos)<<", ";
22 }
23 cout<<endl;
24
25 shared_ptr<int> p = v[9];
26 *p = 100;
27 cout<<*v[9]<<endl;
28
29 return 0;
30 }
说明:
<a>有两种方式可以将shared_ptr应用于标准容器(或者容器适配器等其他容器)
一种是:将容器作为shared_ptr管理的对象,如shared_ptr< list<T> >,使容器可以被安全地共享;
另一种是:将shared_ptr作为容器元素,如vector< shared_ptr<T> >,因为shared_ptr支持拷贝语义和比较操作,符合标准容器对元素的要求,可以实现在容器中安全的容纳元素的指针而不是拷贝。
<b>标准容器不能容纳auto_ptr和scoped_ptr,标准容器可以容纳原始指针,但这就散失了容器的许多好处,因为标准容器无法自动管理类型为指针的元素,必须编写额外的大量的代码来保证指针最终被正确删除,这通常很麻烦且难实现。存储shared_ptr的容器与存储原始指针的容器功能几乎一样,但shared_ptr为程序员做了指针的管理工作,可以任意使用shared_ptr而不用担心资源泄露。
3.3 应用于桥接模式
1 class sample
2 {
3 private:
4 class impl;
5 shared_ptr<impl> p;
6 public:
7 sample();
8 void print():
9 };
10
11 class sample::impl
12 {
13 public:
14 void print()
15 {
16 cout<<"impl print"<<endl;
17 }
18 };
19
20 sample::sample():p(new impl){}
21 void sample::print()
22 {
23 p->print();
24 }
25
26 sample s;
27 s.print();
说明:
<a>先声明一个类sample,它仅向外界暴露了最小的细节,真正的实现在内部类impl,sample用了一个shared_ptr来保存它的指针;
<b>桥接模式是一种结构型的设计模式,它把类的具体实现细节对用户隐藏起来了,以达到类之间的最小耦合关系,也称为pimpl或者handle/body惯用法,它可以将头文件的依赖关系降低到最小,减少编译时间,而且可以实现不使用虚函数实现多态;
<c>scoped_ptr和shared_ptr都可以用来实现桥接模式,但shared_ptr更合适,因为它支持拷贝和赋值,这在很多情况下都是有用的,比如说:配合容器工作。
3.4 应用于工厂模式
1 #include "stdafx.h"
2 #include<iostream>
3 #include<map>
4 #include<memory>
5 #include<vector>
6 #include<cassert>
7 using namespace std;
8
9 #include "boost\smart_ptr.hpp"
10 #include "boost\make_shared.hpp"
11
12 class abstract
13 {
14 public :
15 virtual void f()=0;
16 virtual void g()=0;
17 protected:
18 virtual ~abstract(){}
19 };
20
21 class impl:public abstract
22 {
23 public:
24 virtual void f()
25 {
26 cout<<"class impl f"<<endl;
27 }
28
29 virtual void g()
30 {
31 cout<<"class impl g"<<endl;
32 }
33 };
34
35 shared_ptr<abstract> create()
36 {
37 return shared_ptr<abstract>(new impl);
38 }
39
40
41 int _tmain(int argc, _TCHAR* argv[])
42 {
43 shared_ptr<abstract> p = create();
44 p->f();
45 p->g();
46 return 0;
47 }
说明:
<a>工厂模式是一种创建型的设计模式,包装了new操作符号的使用,使对象的创建工作集中在工厂类或者工厂函数中,从而更容易适应变化,make_shared()就是工厂模式的一个很好的例子;
<b>C++不能高效的返回一个对象,在程序中编写自己的工厂类或工厂函数通常需要在堆上使用new动态分配一个对象,然后返回对象的指针,这种做法不安全,用户容易忘记调用delete,使用shared_ptr就可以解决这个问题;
3.5 定制删除器
class socket_t{...};
socket_t* open_socket()
{
cout<<"open_socket"<<endl;
return new socket_t;
}
void close_socket(socket_t* s)
{
cout<<"close_socket"<<endl;
//。。。其他操作,释放资源
}
socket_t * s = open_socket();
shared_ptr<socket_t> p(s,close_socket);
说明:
<a>这样我们就使用shared_ptr配合定制的删除器管理了socket资源,但离开作用域时,shared_ptr会自动调用close_socket()函数关闭socket,再也不会有资源遗失的担心;
<b>shared_ptr的删除器在处理某些资源时非常有用,它使得用户可以定制、拓展shared_ptr的行为,使得shared_ptr不仅仅能够管理内存资源,而是成为一个"万能"的资源管工具;
<c>shared_ptr(Y* p,D d)的第一个参数是要被管理的指针,第二个参数d则告诉shared_ptr在析构时不是使用delete的操作指针p,而是要用d来操作,即把delete p换成d(p);
<d>这里删除器d可以是一个函数对象,也可以是一个函数指针,只要它能够像函数那样被调用,使得d(p)成立即可。对删除器的要求是它必须是可拷贝的,行为必须也像delete那样,不能抛出异常。shared_ptr提供了一个自由函数get_deleter(shared_ptr<T> const & p),它能够返回函数指针;
3.6 高级议题
3.6.1 shared_ptr<void>能够存储void*型指针,就像一个泛型的指针容器,拥有容纳任意类型的能力,但是将指针存储为void*将丧失原来的类型信息,为了需要的时候正确调用,可以用static_pointer_cast<T>等转型函数重新转为原来的指针,涉及到运行时类型的动态转换,使得代码不安全,不建议使用;
3.6.2 shared_ptr<void>可以实现在退出作用域时调用任意函数。
1 void any_func(void* p)
2 {
3 cout<<"some operate"<<endl;
4 }
5
6 int main()
7 {
8 shared_ptr<void> p((void*)0,any_func);
9 return 0;
10 }
四、shared_array
1、简介:shared_array类似shared_ptr,它包装了new[]操作符在堆上分配的动态数组,同样适用引用计数机制为动态数组提供一个代理,可以在程序的生命周期里长期存在,直到没有任何引用才释放内存。
2、类摘要:
1 template<class T>
2 class shared_array
3 {
4 public:
5 explicit shared_array(T* p = 0);
6 template<class D>shared_array(T* p,D d);
7 ~shared_array();
8
9 shared_array(shared_array const& r);
10
11 shared_array& operator=(shared_array const& r);
12
13 void reset(T* p = 0);
14 template<class D> void reset(T* p,D d);
15
16 T& operator[](std::ptrdiff_t i) const() const;
17 T* get() const;
18
19 bool unique() const;
20 long use_count() const;
21
22 void swap(shared_array<T>& b);
23 };
备注:
<a>构造函数接受指针p必须是new[]的结果,而不能是new表达式的结果;
<b>提供operator[]操作符的重载,可以像普通数组一样用下标访问元素;
<c>没有*,->操作符重载,因为shared_ptr持有的不是一个普通指针;
<d>析构函数使用delete[]释放资源,而不是delete。
3、用法
在使用shared_array重载的operator[]时要小心,shared_array不提供数组索引的范围检查,如果使用了超过动态数组大小的索引将引发可怕的未定义行为。shared_array能力有限,多数情况下它可以用shared_ptr<std::vector>或者std::vector<shared_ptr>来代替,这两个方案具有更好的安全性和更多的灵活性,而所付出的的代价几乎可以忽略不计。
五、weak_ptr
1、简介:weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->。它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。
2、类摘要:
1 template<class T>
2 class weak_ptr
3 {
4 public:
5 weak_ptr();
6 template<class Y>weak_ptr(shared_ptr<Y> const& r);
7 weak_ptr(weak_ptr const& r);
8
9 ~weak_ptr();
10
11 weak_ptr& operator=(weak_ptr const& r);
12
13 long use_count() const;
14
15 bool expired() const;
16
17 shared_ptr<T> lock() const;
18
19 void reset();
20
21 void swap(weak_ptr<T>& b);
22 };
3、用法
<a>weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但是weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,在weak_ptr析构时也不会导致引用计数减少。
<b>使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count() ==0,但更快,表示被观测的资源(也就是shared_ptr管理的资源)已经不复存在;
<c>weak_ptr没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这正是它"弱"的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。但当expired() == true的时候,lock()函数返回一个存储空指针的shared_ptr。
1 shared_ptr<int> sp(new int(10));
2 assert(sp.use_count() == 1);
3
4 weak_ptr<int> wp(sp); //从shared_ptr创建weak_ptr
5 assert(wp.use_count() == 1) //weak_ptr不影响引用计数
6
7 if(!wp.expired()) //判断weak_ptr观察的对象是否失效
8 {
9 shared_ptr<int> sp2 = wp.lock(); //获得一个shared_ptr
10 *sp2 = 100;
11 assert(wp.use_count() == 2);
12 } //退出作用域,sp2自动析构,引用计数减1
13
14 assert(wp.use_count() == 1);
15 sp.reset(); //shared_ptr失效
16 assert(wp.expired());
17 assert(!wp.lock()); //weak_ptr将获得一个空指针
<d>获得this的shared_ptr
说明:weak_ptr的一个重要用途是获得this指针的shared_ptr,使对象自己能够生产shared_ptr管理自己;对象使用weak_ptr观测this指针,这并不影响引用计数,在需要的时候就调用lock()函数,返回一个符合要求的shared_ptr供外界使用。这个解决方案被实现为一个惯用法,在头文件<boost/enable_from_this.hpp>定义一个助手类enable_shared_this<T>,它的声明摘要如下:
1 template<class T>
2 class enable_shared_from_this
3 {
4 public:
5 enable_shared_from_this();
6 };
使用的时候只需要让相被shared_ptr管理的类从它继承即可,成员函数shared_from_this()会返回this的shared_ptr。
1 #include <boost/enable_shared_from_this.hpp>
2 #include <boost/make_shared.hpp>
3
4 class self_shared:public enable_shared_from_this<self_shared>
5 {
6 public:
7 self_shared(int n):x(n){}
8 int x;
9 void print()
10 {
11 cout<<"self_shared:"<<x<<endl;
12 }
13 };
14
15 int main()
16 {
17 shared_ptr<self_shared> sp = make_shared<self_shared>(314);
18 sp->print();
19 shared_ptr<self_shared> p = sp->shared_from_this();
20 p->x = 1000;
21 p->print();
22 }
备注:千万不能从一个普通对象(非shared_ptr)使用shared_from_this()获取shared_ptr
六、instrusive_ptr
1、简介:是一个侵入式的引用计数型指针,不推荐使用。
七、pool库
1、简述:boost.pool库基于简单分隔存储思想实现了一个快速、紧凑的内存池库,不仅能够管理大量的对象,还可以被用作STL的内存分配器。从某种程度上讲,它近似于一个小型的垃圾回收机制,在需要大量地分配/释放小对象时很有效率,而且完全不需要考虑delete。pool库包含是个组成部分:最简单的pool,分配类实例的object_pool、单件内存池singleton_pool和可以用于标准库的pool_alloc。
2、pool
2.1、简介:最简单也最容易使用的内存池类,可以返回简单类型数据的内存指针。
2.2、类摘要
1 template<typename UserAllocator=...>
2 class pool
3 {
4 public:
5 explicit pool(size_type requested_size);
6 ~pool();
7 size_type get_requested_size() const;
8
9 void* malloc();
10 void* ordered_malloc();
11 void* ordered_malloc(size_type n);
12 bool is_from(void* chunk) const;
13
14 void free(void * chunk);
15 void ordered_free(void* chunk);
16 void free(void* chunks,size_type n);
17 void ordered_free(void* chunks,size_type n);
18
19 bool release_memory();
20 bool purge_memory();
21
22 };
说明:
<a>pool模板类型参数UserAllocator是一个用户定义的内存分配器,它实现了特定的内存分配算法,通常可以直接用默认的default_user_allocator_new_delete。
<b>pool的构造函数接受一个size_type类型的整数requested_size,指示每次pool分配内存块的大小(而不是pool内存池的大小),这个值可以用get_requested_size()获得。pool会根据需要自动地向系统申请或归还使用的内存,在析构,pool将自动释放它所持有所有内存块。
<c>成员函数malloc()和order_malloc()的行为很类似C中的全局函数malloc(),用void*指针返回从内存池中分配的内存块,大小为构造函数中指定的requested_size。如果内存分配失败,函数会返回0,不会抛出异常。malloc()从内存池中任意分配一个内存块,而order_malloc()则在分配的同时合并空闲块链表。order_malloc()带参数的形式还可以连续分配n块的内存。分配后的内存块可以用is_from()函数测试是否从这个内存池分配出去的。
<d>与malloc()对应的一组函数是free(),用来手工释放之前分配的内存块,这些内存块必须是从这个内存池分配出去的(is_from(chunk) == true),一般情况内存池会自动管理内存分配,不应该调用free()函数,除非你认为内存池的空间不足,必须释放已经分配的内存。
<e>release_memory()让内存池释放所有未被分配的内存,但已分配的内存块不受影响:purge_memory()则强制释放pool持有的所有内存,不管内存块是否被使用。实际上,pool的析构函数就是调用purge_memory()。这个函数一般情况下也不应该由程序员手工调用。
2.3 用法
1 #include <boost/pool/pool.hpp>
2 using namespace boost;
3
4 int main()
5 {
6 pool<> pl(sizeof(int));
7
8 int* p = (int*)p1.malloc();
9 assert(p1.is_from(p));
10
11 p1.free(p);
12 for(int i-0;i<100;i++)
13 {
14 p1.order_malloc(10);
15 }
16 }
备注:
<a>pool分配内存失败不会抛出异常,实际编写的代码应该检查malloc()函数返回的指针,防止空指针错误。
<b>它只能作为普通数据类型如int、double等的内存池,不能应用于复杂的类和对象,因为它只分配内存,不调用构造函数,这个时候我们需要用object_pool。
3、object_pool
3.1 简介:用于类实例(对象)的内存池,会在析构时对所有已经分配的内存块调用析构函数。需包含头文件:#include<boost/pool/object_pool.hpp> using namspace boost;
3.2 类摘要
1 template<typename ElementType>
2 class object_pool:protected pool
3 {
4 public:
5 object_pool();
6 ~object_pool();
7
8 element_type* malloc();
9 void free(element_type* p);
10 bool is_from(element_type* p) const;
11
12 element_type* construct(...);
13 void destroy(element_type* p);
14 };
备注:
<a>object_pool要分配的元素类型,要求其析构函数不能抛出异常,一旦在模板中指定了类型,object_pool实例就不再用于分配其他类型的对象。
<b>object_pool特殊之处是construct()和destroy()函数,construct()实际上是一组函数,有多个函数的重载形式,它先调用malloc()分配内存,然后再在内存块上使用传入的参数调用类的构造函数,返回的是一个已经初始化的对象指针。destroy()则先调用对象的析构函数,然后再用free()释放内存块。这些函数都不会抛出异常,如果内存分配失败,将返回0;
3.3 用法
1 #include<boost/pool/object_pool.hpp>
2 using namespace boost;
3
4 struct demo_class
5 {
6 public:
7 int a,b,c;
8 demo_class(int x = 1,int y =2,int z = 3):a(x),b(y),c(z){}
9 };
10
11 int main()
12 {
13 object_pool<demo_class> p1; //对象内存池
14
15 demo_class* p = p1.malloc(); //分配一个原始内存块
16 assert(p1.if_from(p));
17
18
19 //p指向的内存未经过初始化
20 assert(p->a != 1 || p->b != 2 || p->c != 3);
21
22 //构造一个对象,可以传递参数
23 p = p1.construct(7,8,9);
24 assert(p->a == 7);
25
26 //定义一个分配string对象的内存池
27 object_pool<string> pls;
28 //连续大量分配string对象
29 for (int i=0;i<10;++i)
30 {
31 string* ps = pls.construct("hello object_pool");
32 cout<<*ps<<endl;
33 }
34
35 return 0;
36 }
备注:默认情况下,在使用object_pool的construct()的时候我们最多用三个参数来创建对象,但construct被设计为可拓展的,它基于宏处理m4实现了一个拓展机制,可以自动生成接收任意数量参数的construct函数。
4、singleton_pool
4.1、简介:singleton_pool与pool的接口完全一致,可以分配简单数据类型(POD)的内存指针,但它是一个单件,并提供线程安全。目前boost还未提供标准的单件库,singleton_pool在其内部实现了一个较简单、泛型的单间类,保证main()函数运行之前就创建单件。需包含一下头文件:#include<boost/pool/singleton_pool.hpp> using namespace boost;
4.2、类摘要
1 template<typename Tag,unsigned RequestedSize>
2 class singleton_pool
3 {
4 public:
5 static bool is_from(void* ptr);
6 static void* malloc();
7 static void* ordered_malloc();
8 static void* ordered_malloc(size_type n);
9
10 static void free(void* ptr);
11 static void ordered_free(void* ptr);
12 static void free(void* ptr,std::size_t n);
13 static void ordered_free(void* ptr,size_type n);
14
15 static bool release_memory();
16 static bool purge_memory();
17 };
备注:
<a>singleton_pool主要是有两个摸板类型参数(其余的可以使用缺省值)。第一个Tag用于标记不同的单件,可以是空类,甚至是声明。第二个参数RequestedSize等同于pool构造函数中的整数requested_size,指示pool分配内存块的大小。
<b>singleton_pool接口与pool完全一致,但函数均是静态的,因此不需要声明singleton_pool的实例,直接用域操作符::来调用静态成员函数。因为singleton_pool是单件,所以它的生命周期与整个程序同样长,除非手动调用release_memory()或purge_memory(),否则singleton_pool不会自动释放所占用的内存。除了这两点,singleton_pool的用法与pool完全相同。
4.3 用法
1 #include <boost/pool/singleton_pool>
2 using namespace boost;
3
4 struct pool tag{};
5
6 typedef singleton_pool<pool_tag,sizeof(int)> sp1;
7
8 int main()
9 {
10 int * p = (int*)sp1::malloc();
11 assert(sp1::is_from(p));
12 sp1::release_memory();
13
14 return 0;
15 }
5、pool_alloc
5.1 简介:pool_alloc提供了两个可以用于标准容器模板参数的内存分配器,分别是pool_alloc()和fast_pool_allocator,它们的行为与之前的内存池类有一点不同---当内存分配失败时会抛出异常std::bad_alloc。它们位于名字空间boost,需要包含头文<boost/pool/pool_alloc.hpp>。除非有特殊的需求,我们应该总使用STL实现自带的内存分配器,使用pool_alloc需要经过仔细测试,以保证它和容器可以共同工作。
5.2 用法
1 #include <boost/pool/pool_alloc.hpp>
2 using namespace boost;
3
4 int main()
5 {
6 vector< int,pool_allocator<int> > v; //使用pool_allocator代替标准容器默认的内存分配器
7 v.push_back(10); //vector将使用新的分配器良好工作
8 cout<v.size();
9 }