Boost库学习(3) 内存管理

摘要:
scoped_Ptr和auto_Ptr的基本区别是指针所有权,auto_Ptr旨在在函数之间转移指针的所有权。同时,只能有一个auto_Ptr管理指针,而scoped_Ptr将复制构造函数和赋值函数声明为私有,并拒绝指针所有权的转移,除了scoped_ Ptr本身,其他任何人都无权访问托管指针,从而确保了指针的绝对安全。

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、类摘要:

View Code
 1 template<class T>
 2 classscoped_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、类摘要:

View Code
1 template<class T>
2 classscoped_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、类摘要:

View Code
1 template<class T>
2 classshared_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     voidreset();
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出现的不对称性)

View Code
1 #include "stdafx.h"
2 #include<iostream>
3 #include<map>
4 #include<memory>
5 #include<vector>
6 #include<cassert>
7 using namespacestd;
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 应用于标准容器

View Code
1 #include "stdafx.h"
2 #include<iostream>
3 #include<map>
4 #include<memory>
5 #include<vector>
6 #include<cassert>
7 using namespacestd;
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 应用于桥接模式

View Code
1 classsample
2 {
3 private:
4     classimpl;                    
5     shared_ptr<impl>p;
6 public:
7 sample();
8     voidprint():
9 };
10 
11 classsample::impl
12 {
13 public:
14     voidprint()
15 {
16         cout<<"impl print"<<endl;
17 }
18 };
19 
20 sample::sample():p(newimpl){}
21 voidsample::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 应用于工厂模式

View Code
1 #include "stdafx.h"
2 #include<iostream>
3 #include<map>
4 #include<memory>
5 #include<vector>
6 #include<cassert>
7 using namespacestd;
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 voidf()
25 {
26         cout<<"class impl f"<<endl;
27 }
28 
29     virtual voidg()
30 {
31         cout<<"class impl g"<<endl;
32 }
33 };
34 
35 shared_ptr<abstract>create()
36 {
37     return shared_ptr<abstract>(newimpl);
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 定制删除器

View Code
classsocket_t{...};

socket_t*open_socket()
{
    cout<<"open_socket"<<endl;
    return newsocket_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>可以实现在退出作用域时调用任意函数。

View Code
1 void any_func(void*p)
2 {
3     cout<<"some operate"<<endl;
4 }
5 
6 intmain()
7 {
8     shared_ptr<void> p((void*)0,any_func);
9     return 0;
10 }

四、shared_array

1、简介:shared_array类似shared_ptr,它包装了new[]操作符在堆上分配的动态数组,同样适用引用计数机制为动态数组提供一个代理,可以在程序的生命周期里长期存在,直到没有任何引用才释放内存。

2、类摘要:

View Code
1 template<class T>
2 classshared_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、类摘要

View Code
1 template<class T>
2 classweak_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     voidreset();
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。

View Code
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>,它的声明摘要如下:

View Code
1 template<class T>
2 classenable_shared_from_this
3 {
4     public:
5 enable_shared_from_this();
6 };

使用的时候只需要让相被shared_ptr管理的类从它继承即可,成员函数shared_from_this()会返回this的shared_ptr。

View Code
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(intn):x(n){}
8     intx;
9     voidprint()
10 {
11         cout<<"self_shared:"<<x<<endl;
12 }
13 };
14 
15 intmain()
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、类摘要

View Code
1 template<typename UserAllocator=...>
2 classpool
3 {
4     public:
5         explicitpool(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         boolrelease_memory();
20         boolpurge_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 用法

View Code
1 #include <boost/pool/pool.hpp>
2 using namespaceboost;
3 
4 intmain()
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 类摘要

View Code
1 template<typename ElementType>
2 class object_pool:protectedpool
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 用法

View Code
1 #include<boost/pool/object_pool.hpp>
2 using namespaceboost;
3 
4 structdemo_class
5 {
6 public:
7     inta,b,c;
8     demo_class(int x = 1,int y  =2,int z = 3):a(x),b(y),c(z){}
9 };
10 
11 intmain()
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、类摘要

View Code
1 template<typename Tag,unsigned RequestedSize>
2 classsingleton_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 boolrelease_memory();
16     static boolpurge_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 用法

View Code
1 #include <boost/pool/singleton_pool>
2 using namespaceboost;
3 
4 structpool tag{};
5 
6 typedef singleton_pool<pool_tag,sizeof(int)>sp1;
7 
8 intmain()
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 用法

View Code
1 #include <boost/pool/pool_alloc.hpp>
2 using namespaceboost;
3 
4 intmain()
5 {
6     vector< int,pool_allocator<int> > v;    //使用pool_allocator代替标准容器默认的内存分配器
7     v.push_back(10);                        //vector将使用新的分配器良好工作
8     cout<v.size();
9 }

免责声明:文章转载自《Boost库学习(3) 内存管理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇MATLAB中导入数据:importdata函数ES数据架构与关系数据库Mysql下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Delphi 中多线程同步的一些处理方法

当创建了多个线程,并且多个线程都要访问同一资源,,就有可能出现混乱,于是用Synchronize来控制,使同一时间只有一个线程使用那部分资源,Synchronize参数里面的代码就是多线程需要公共的代码!线程是进程内一个相对独立的、可调度的执行单元。一个应用可以有一个主线程,一个主线程可以有多个子线程,子线程还可以有自己的子线程,这样就构成了多线程应用了。...

c语言进阶6-指针

指针是c语言的一个重要组成部分 是c语言的核心、精髓所在,用好指针可以在c语言编程中起到事半功倍的效果。一方面,可以提高程序的编译效率和执行速度以及实现动态的存储分配;另一方面,使用指针可使程序更灵活,全球表示各种数据结构,编写高质量的程序。 指针是c语言显著的优点之一,其使用起来十分灵活而且能提高某些程序的效率,但是如果使用不当则很容易造成系统错误。许多...

Windows内存小结(有好多图,比较清楚)

以前写过一篇理解程序内存, 当时主要是针对用户态,下面再稍微深入一点: 我们以32位程序为例(不启用AWE), 总共4G虚拟空间,其中低2G属于用户态, 高2G属于操作系统内核, 每个程序都有自己的低2G用户空间, 高2G内核空间是所有程序共享的。高2G内核空间中, 属于同一Session的程序又共享相同的session空间: x86系统所有的内存以...

ANDROID窗体管理服务实现机制和架构分析

 一、功能     窗体管理是ANDROID框架一个重要部分,主要包含例如以下功能:    (1)Z-ordered的维护   (2)窗体的创建、销毁   (3)窗体的绘制、布局    (4)Token管理,AppToken    (5)活动窗体管理(FocusWindow)    (6)活动应用管理(FocusAPP)    (7)输入法管理  ...

FFMPEG 内部 YUV444p16LE-&amp;gt; P016LE

y 方向直接复制 1. hscale 2. vscale static void yuv2p016cX_c(SwsContext *c, const int16_t *chrFilter, int chrFilterSize, const int16_t **chrUSrc, const int16_t *...

标准控件的使用技巧与自绘控件

在您决定开发 Windows 提供的常规免费自定义控件范围之外的控件之后,您必需确定自己的控件将有多少独到之处 — 在功能和外观两方面。例如,我们假定您正在创建一个类似于计速表的控件。由于公共控件库 (ComCtrl32.dll) 中没有类似的控件,您完全需要自己进行以下操作:编写所有控件功能需要的代码,进行绘制,默认终端用户的交互,以及控件与其父窗口之间...