Boost智能指针使用总结

  内存管理是一个比较繁琐的问题,C++中有两个实现方案: 垃圾回收机制智能指针。垃圾回收机制由于性能等缘由不被C++的大佬们推崇, 而智能指针被认为是解决C++内存问题的最优方案。html

1. 智能指针定义

     一个智能指针就是一个C++的对象, 这对象的行为像一个指针,可是它却能够在其不须要的时候自动删除。注意这个“其不须要的时候”, 这可不是一个精确的定义。这个不须要的时候能够指好多方面:局部变量退出函数做用域、类的对象被析构……。因此boost定义了多个不一样的智能指针来管理不一样的场景。ios

shared_ptr<T> 内部维护一个引用计数器来判断此指针是否是须要被释放。是boost中最经常使用的智能指针了。
scoped_ptr<t> 当这个指针的做用域消失以后自动释放
intrusive_ptr<T> 也维护一个引用计数器,比shared_ptr有更好的性能。可是要求T本身提供这个计数器。
weak_ptr<T> 弱指针,要和shared_ptr 结合使用
shared_array<T> 和shared_ptr类似,可是访问的是数组
scoped_array<T> 和scoped_ptr类似,可是访问的是数组

2. Boost::scoped_ptr<T>

2.1 定义

    scoped_ptr 是boost中最简单的智能指针。scoped_ptr的目的也是很简单, 当一个指针离开其做用域时候,释放相关资源。特别注意的必定就是scoped_ptr 不能共享指针的全部权不能转移全部权。也就是说这个内存地址就只能给的声明的变量用,不能给其余使用。c++

2.2 特色

(1)scoped_ptr的效率和空间的消耗内置的指针差很少程序员

(2)scoped_ptr不能用于管理数组对象,不能指向一块可以动态增加的内存区域(用scoped_array代替)数组

(3)scoped_ptr不能转换全部权,所以不能做为函数的返回值安全

(4)scoped_ptr不能共享全部权,所以不能用于stl的容器中(用shared_ptr代替)多线程

2.3 使用原则

(1)在可能有异常抛出的做用域使用指针ide

(2)函数里有几条控制路径函数

(3)动态分配对象的生存期应被限制于特定的做用域内性能

(4)异常安全很是重要时(总应如此!)

2.4 例子

 1 class test 
 2 { 
 3 public: 
 4     void print() 
 5     { 
 6         cout << "test print now" <<endl; 
 7     } 
 8 };
 9 int _tmain(int argc, _TCHAR* argv[]) 
10 { 
11     boost::scoped_ptr<test> x(new test); 
12     x->print(); 
13     return 0; 
14 }
View Code

3.Boost::shared_ptr<T>

3.1 定义

  boost::shared_ptr是能够共享全部权的智能指针.

3.2 特色

(1)boost::shared_ptr在内部维护一个引用计数器, 当有一个指针指向这块内存区域引用计数+1, 反之-1, 若是没有任何指针指向这块区域, 引用计数器为0,释放内存区域

(2)boost::shared_ptr能够共享和转移全部权

(3)boost::shared_ptr能够被标准库的容器所使用

(4)boost::shared_ptr是线程安全的,这点在多线程程序中也很是重要

(5)boost::shared_ptr不能指向一块动态增加的内存(用share_array代替)

3.3 使用原则

(1)避免对shared_ptr所管理的对象的直接内存管理操做,以避免形成该对象的重释放

(2)shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各类引用计数管理内存方式的通病)

(3)不要构造一个临时的shared_ptr做为函数的参数

3.4 例子

1 int _tmain(int argc, _TCHAR* argv[]) 
2 { 
3     boost::shared_ptr<test> ptr_1(new test); 
4     ptr_1->print();//引用计数为1
5     boost::shared_ptr<test> ptr_2 = ptr_1; 
6     ptr_2->print();//引用计数为2
7     ptr_1->print();// 引用计数仍是为2
8     return 0; 
9 }
View Code

4. Boost::intrusive_ptr<T>

4.1 定义

  boost::intrusive_ptr一种“侵入式”的引用计数指针,它实际并不提供引用计数功能,而是要被存储的对象本身实现引用计数功能,并提供intrusive_ptr_add_refintrusive_ptr_release函数接口供boost::intrusive_ptr调用。

4.2 使用原则

(1)你须要把 this看成智能指针来使用

(2)已有代码使用或提供了插入式的引用计数

(3)智能指针的大小必须与裸指针的大小相等

4.3 例子

  下面经过一个具体的例子来讲明boost::intrusive_ptr的用法,首先实现一个基类intrusive_ptr_base,定义intrusive_ptr_add_ref和intrusive_ptr_release函数来提供引用计数功能。

 1 /**
 2 * intrusive_ptr_base基类,提供intrusive_ptr_add_ref()和intrusive_ptr_release()函数来提供引用计数功能;
 3 * 使用boost::intrusive_ptr指针存储的用户类类型必须继承自intrusive_ptr_base基类。
 4 */
 5 #include <ostream>
 6 #include <boost/checked_delete.hpp>
 7 #include <boost/detail/atomic_count.hpp>
 8  
 9  
10 template<class T>
11 class intrusive_ptr_base {
12 public:
13     /**
14     * 缺省构造函数
15     */
16     intrusive_ptr_base(): ref_count(0) {
17         std::cout << "  Default constructor " << std::endl;
18     }
19      
20     /**
21     * 不容许拷贝构造,只能使用intrusive_ptr来构造另外一个intrusive_ptr
22     */
23     intrusive_ptr_base(intrusive_ptr_base<T> const&): ref_count(0) {
24         std::cout << "  Copy constructor..." << std::endl;
25     }
26      
27     /**
28     * 不容许进行赋值操做
29     */
30     intrusive_ptr_base& operator=(intrusive_ptr_base const& rhs) {
31         std::cout << "  Assignment operator..." << std::endl;
32         return *this;
33     }
34      
35     /**
36     * 递增引用计数(放到基类中以便compiler能找到,不然须要放到boost名字空间中)
37     */
38     friend void intrusive_ptr_add_ref(intrusive_ptr_base<T> const* s) {
39         std::cout << "  intrusive_ptr_add_ref..." << std::endl;
40         assert(s->ref_count >= 0);
41         assert(s != 0);
42         ++s->ref_count;
43     }
44  
45     /**
46     * 递减引用计数
47     */
48     friend void intrusive_ptr_release(intrusive_ptr_base<T> const* s) {
49         std::cout << "  intrusive_ptr_release..." << std::endl;
50         assert(s->ref_count > 0);
51         assert(s != 0);
52         if (--s->ref_count == 0)
53             boost::checked_delete(static_cast<T const*>(s));  //s的实际类型就是T,intrusive_ptr_base<T>为基类
54     }
55      
56     /**
57     * 相似于shared_from_this()函数
58     */
59     boost::intrusive_ptr<T> self() {
60         return boost::intrusive_ptr<T>((T*)this);
61     }
62      
63     boost::intrusive_ptr<const T> self() const {
64         return boost::intrusive_ptr<const T>((T const*)this);
65     }
66      
67     int refcount() const {
68         return ref_count;
69     }
70      
71 private:
72     ///should be modifiable even from const intrusive_ptr objects
73     mutable boost::detail::atomic_count ref_count;
74  
75 };
View Code

  用户类类型须要继承intrusive_ptr_base基类,以便具备引用计数功能。

 1 #include <iostream>
 2 #include <string>
 3 #include <boost/intrusive_ptr.hpp>
 4 #include "intrusive_ptr_base.hpp"
 5  
 6 /**
 7 * 用户类类型继承自intrusive_ptr_base,该实现方式相似于boost::enable_shared_from_this<Y>
 8 */
 9 class Connection : public intrusive_ptr_base< Connection > {
10 public:
11     /**
12     * 构造函数,调用intrusive_ptr_base< Connection >的缺省构造函数来初始化对象的基类部分
13     */
14     Connection(int id, std::string tag):
15         connection_id( id ), connection_tag( tag ) {}
16  
17     /**
18     * 拷贝构造函数,只复制自身数据,不能复制引用计数部分
19     */
20     Connection(const Connection& rhs):
21         connection_id( rhs.connection_id ), connection_tag( rhs.connection_tag) {}
22      
23     /**
24     * 赋值操做,一样不能复制引用计数部分
25     */
26     const Connection operator=( const Connection& rhs) {
27         if (this != &rhs) {
28             connection_id = rhs.connection_id;
29             connection_tag = rhs.connection_tag;
30         }
31          
32         return *this;
33     }
34  
35 private:
36     int connection_id;
37     std::string connection_tag;
38 };
39  
40 int main() {
41     std::cout << "Create an intrusive ptr" << std::endl;
42     boost::intrusive_ptr< Connection > con0 (new Connection(4, "sss") );  //调用intrusive_ptr_add_ref()递增引用计数
43     std::cout << "Create an intrusive ptr. Refcount = " << con0->refcount() << std::endl;
44  
45     boost::intrusive_ptr< Connection > con1 (con0);   //调用intrusive_ptr_add_ref()
46     std::cout << "Create an intrusive ptr. Refcount = " << con1->refcount() << std::endl;
47     boost::intrusive_ptr< Connection > con2 = con0;   //调用intrusive_ptr_add_ref()
48     std::cout << "Create an intrusive ptr. Refcount = " << con2->refcount() << std::endl;
49      
50     std::cout << "Destroy an intrusive ptr" << std::endl;
51  
52     return 0;
53 }
View Code

 

程序运行输出:
Create an intrusive ptr
Default constructor 
intrusive_ptr_add_ref...
Create an intrusive ptr. Refcount = 1
intrusive_ptr_add_ref...
Create an intrusive ptr. Refcount = 2
intrusive_ptr_add_ref...
Create an intrusive ptr. Refcount = 3
Destroy an intrusive ptr
intrusive_ptr_release...
intrusive_ptr_release...
intrusive_ptr_release...

4.4 boost::intrusive_ptr与boost::shared_ptr区别

  使用boost::shared_ptr用户类自己不须要具备引用计数功能,而是由boost::shared_ptr来提供;使用boost::shared_ptr的一大陷阱就是用一个raw pointer屡次建立boost::shared_ptr,这将致使boost::shared_ptr析构时该raw pointer被屡次销毁当。即不能以下使用:

1 int *a = new int(5);
2 boost::shared_ptr ptr1(a);
3 boost::shared_ptr ptr2(a);  //错误!
View Code

  boost::intrusive_ptr彻底具有boost::shared_ptr的功能,且不存在shared_ptr的问题,便可以利用raw pointer建立多个intrusive _ptr,其缘由就在于引用计数的ref_count对象shared_ptr是放在shared_ptr结构里,而目标对象T经过继承intrusive_ptr_base引用计数做为T对象内部成员变量,就不会出现一个对象有两个引用计数器的状况出现。
  那么为何一般鼓励你们使用shared_ptr,而不是intrusive_ptr呢, 在于shared_ptr不是侵入性的,能够指向任意类型的对象; 而intrusive_ptr所要指向的对象,须要继承intrusive_ptr_base,即便不须要引用计数成员也会被建立。

4.5 结论

  若是建立新类且须要进行传递,则继承intrusive_ptr_base使用intrusive_ptr

5. Boost::weak_ptr<T>

5.1 定义

    weak_ptr 就是一个弱指针。weak_ptr 被shared_ptr控制, 它能够经过share_ptr的构造函数或者lock成员函数转化为share_ptr

5.2 特色

(1)weak_ptr的一个最大特色就是它共享一个share_ptr的内存

(2)不管是构造仍是析构一个weak_ptr都不会影响引用计数器

5.3 弱引用与强引用

  一个强引用被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。相对而言,弱引用引用的对象活着的时候不必定存在。仅仅是当它存在的时候的一个引用。弱引用不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上相似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存

 1 boost::weak_ptr
 2 
 3 boost::weak_ptr<T>是boost提供的一个弱引用的智能指针,它的声明能够简化以下:
 4 
 5 namespace boost {
 6 
 7     template<typename T> class weak_ptr {
 8     public:
 9         template <typename Y>
10         weak_ptr(const shared_ptr<Y>& r);
11 
12         weak_ptr(const weak_ptr& r);
13 
14         ~weak_ptr();
15 
16         T* get() const; 
17         bool expired() const; 
18         shared_ptr<T> lock() const;
19     }; 
20 }
View Code

  能够看到,boost::weak_ptr必须从一个boost::share_ptr或另外一个boost::weak_ptr转换而来,这也说明,进行该对象的内存管理的是那个强引用的boost::share_ptrboost::weak_ptr只是提供了对管理对象的一个访问手段

  boost::weak_ptr除了对所管理对象基本访问功能(经过get()函数)外,还有两个经常使用的功能函数:expired()用于检测所管理的对象是否已经释放lock()用于获取所管理的对象的强引用指针

5.4 循环引用

5.4.1 循环定义

  引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象

5.4.2 循环引用例子

 1 #include <string>
 2 #include <iostream>
 3 #include <boost/shared_ptr.hpp>
 4 #include <boost/weak_ptr.hpp>
 5 
 6 class parent;
 7 class children;
 8 
 9 typedef boost::shared_ptr<parent> parent_ptr;
10 typedef boost::shared_ptr<children> children_ptr;
11 
12 class parent
13 {
14 public:
15     ~parent() { std::cout <<"destroying parent\n"; }
16 
17 public:
18     children_ptr children;
19 };
20 
21 class children
22 {
23 public:
24     ~children() { std::cout <<"destroying children\n"; }
25 
26 public:
27     parent_ptr parent;
28 };
29 
30 
31 void test()
32 {
33     parent_ptr father(new parent());
34     children_ptr son(new children);
35 
36     father->children = son;
37     son->parent = father;
38 }
39 
40 void main()
41 {
42     std::cout<<"begin test...\n";
43     test();
44     std::cout<<"end test.\n";
45 }
View Code

  运行该程序能够看到,即便退出了test函数后,因为parent和children对象互相引用,它们的引用计数都是1,不能自动释放,而且此时这两个对象再没法访问到。这就引发了c++中那臭名昭著的内存泄漏。

通常来说,解除这种循环引用有下面有三种可行的方法

(1)当只剩下最后一个引用的时候须要手动打破循环引用释放对象

(2)当parent的生存期超过children的生存期的时候,children改成使用一个普通指针指向parent

(3)使用弱引用的智能指针打破这种循环引用

虽然这三种方法均可行,但方法1和方法2都须要程序员手动控制,麻烦且容易出错。这里主要介绍一下第三种方法和boost中的弱引用的智能指针boost::weak_ptr。

5.4.3 经过boost::weak_ptr来打破循环引用

  因为弱引用不更改引用计数,相似普通指针,只要把循环引用的一方使用弱引用,便可解除循环引用。对于上面的那个例子来讲,只要把children的定义改成以下方式,便可解除循环引用:

1 class children
2 {
3 public:
4     ~children() { std::cout <<"destroying children\n"; }
5 
6 public:
7     boost::weak_ptr<parent> parent;
8 };
View Code

  最后值得一提的是,虽然经过弱引用指针能够有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的状况下才能使用,也能够是说这个仅仅是一种编译期的解决方案,若是程序在运行过程当中出现了循环引用,仍是会形成内存泄漏的。所以,不要认为只要使用了智能指针便能杜绝内存泄漏。毕竟,对于C++来讲,因为没有垃圾回收机制,内存泄漏对每个程序员来讲都是一个很是头痛的问题。

5.5 使用原则

(1)要打破递归的依赖关系

(2)使用一个共享的资源不须要共享全部权

(3)避免悬空的指针

(4)shared_ptr构造weak_ptr时,weak_ptr所指内存为空会抛出异常,而weak_ptr的lock()成员不会抛出异常但会返回个空指针,根据本身需求选择

5.6 例子

 1 int _tmain(int argc, _TCHAR* argv[]) 
 2 { 
 3     boost::shared_ptr<test> sharePtr(new test);;
 4     boost::weak_ptr<test> weakPtr(sharePtr); 
 5     //weakPtr 就是用來保存指向這塊內存區域的指針的 
 6     //干了一大堆其余事情
 7     boost::shared_ptr<test> sharePtr_2 = weakPtr.lock(); 
 8     if (sharePtr_2) 
 9         sharePtr_2->print();
10     return 0; 
11 }
View Code

6. Boost::shared_array<T> 和Boost::scoped_array<T>

6.1 定义

    前面提到过shared_ptr和scoped_ptr不能用于数组的内存(new []),因此shared_array和scoped_array就是他们的代替品。

6.2 例子

1 int _tmain(int argc, _TCHAR* argv[]) 
2 { 
3     const int size = 10; 
4     boost::shared_array<test> a(new test[]);
5     for (int i = 0; i < size; ++i) 
6         a[i].print();
7     return 0; 
8 }
View Code

7.std::auto_ptr

7.1 定义

  auto_ptr是C++标准库里的类,它接受一个类型形参的模板,为动态分配的对象提供异常安全。其实,它的核心思想是:用一个对象存储须要被自动释放的资源,而后依靠对象的析构函数来释放资源

7.2 特色

(1)auto_ptr的构造函数带explicit 关键字,必须使用初始化的直接形式建立auto_ptr对象。        

1 auto_ptr<int> ap(new int(1024));  //ok   
2 auto_ptr<int> ap=new int(1024);   //error
View Code

(2)auto_ptr 在析构函数中释放了动态分配的空间,所以能自动释放内存。下面函数只动态分配了内存,并无显示释放。可是编译器保证在展开栈越过f以前运行pi的析构函数。

1 void f() { auto_ptr<int> ap(new int(1024)); } 
View Code

(3)auto_ptr重载了解引用操做符箭头操做符,支持了普通指针的行为。

(4)赋值时删除了左操做数指向的对象

1 auto_ptr<int> ap1(new int(1024));  auto_ptr<int> ap2;
2 ap2=ap1;
View Code

       将ap1赋值给ap2后,删除了ap2原来指的对象;ap2置为指向ap1所指的对象;ap1为未绑定对象。可看代码。

(5)测试auto_ptr对象,能够调用get成员函数,该函数返回包含在auto_ptr对象中的基础指针。

1 if(ap.get())  *ap=512;   //ok          
2 if(ap) *ap=512;             //error
View Code

7.3 使用原则

尽管auto_ptr类模板为处理动态分配的内存提供了安全性和便利性的尺度,可是也存在很多缺陷,接下来结合例子给出auto_ptr的一些缺陷。

(1)不要使用auto_ptr对象保存指向静态分配对象的指针。不然,当auto_ptr对象自己被撤销时,它将试图删除指向非动态分配对象的指针,致使未定义的行为。

1 int a=1;
2 auto_ptr<int> ap(&a);  //编译没有问题,会致使未定义行为
View Code

(2)不要使两个auto_ptr对象指向同一对象

1 auto_ptr<int> ap1(new int (1024));
2 auto_ptr<int> ap2(ap1.get());
View Code

(3)不要使用auto_ptr对象保存指向动态分配数组的指针。从源代码中能够看出,它用的是delete操做符,而不是delete [ ] 操做符

(4)不要auto_ptr对象存储在容器中。由于auto_ptr的复制赋值具备破坏性。不知足容器要求:复制或赋值后,两个对象必须具备相同值

7.4 例子

 1 #include <utility>
 2 #include <iostream>
 3 using namespace std;
 4  
 5 class A
 6 {
 7 public:
 8     A() { id = ++count; cout << "create A" << id  <<  "\n"; }
 9     ~A() { cout << "destroy A" << id << "\n"; }
10 private:
11     static int count;
12     int id;
13 };
14  
15 int A::count = 0;
16  
17 /*调用该函数会丢失掉全部权*/
18 void sink(auto_ptr<A> a)
19 {
20     cout << "Enter sink()\n";
21 }
22  
23 /*调用该函数会建立对象,并获取全部权*/
24 auto_ptr<A> create()
25 {
26     cout << "Enter create()\n";
27     auto_ptr<A> a(new A());
28     return a;
29 }
30  
31 int main(int argc, char *argv[])
32 {
33     auto_ptr<A> a1 = create();
34     auto_ptr<A> a2 = a1; /*转移全部权,此时a1无效了*/
35     auto_ptr<A> a3(new A());
36     cout << "Exit create()\n";
37     sink(a2);/*丢失全部权,会发现a2的释放在sink函数中进行*/
38     cout << "Exit sink()\n";
39     return 0;
40 }
41  
42 输出结果是:<br>Enter create()<br>create A1<br>create A2<br>Exit create()<br>Enter sink()<br>destroy A1<br>Exit sink()<br>destroy A2<br><br>
View Code

8. 使用智能指针的几个注意点

(1)声明一个智能指针的时候要当即给它实例化, 并且必定不能手动释放它。

(2)…_ptr<T> 不是T* 类型。因此:

          a: 声明的时候要…_ptr<T> 而不是….._ptr<T*>。

          b:不能把T* 型的指针赋值给它。

          c: 不能写ptr=NULL, 而用ptr.reset()代替。

(3)不能循环引用

(4)不要声明临时的share_ptr, 而后把这个指针传递给一个函数

 

原文连接:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html