【转帖】smart pointer

    内存管理是一个比较繁琐的问题,C++中有两个实现方案:垃圾回收机制、智能指针

    一个智能指针就是一个C++的对象,这对象的行为像一个指针,但是它却可以在其不需要的时候(不是一个精确的定义,如:局部变量退出函数作用域、类的对象被析构...等)自动删除注意:(1)声明一个智能指针时要立即给它实例化,且一定不能手动释放它;(2)..._ptr<T>不是T*类型,所以声明时要..._ptr<T>而不是...ptr<T*>,不能把T*型的指针赋值给它,且不能些ptr=NULL,而用ptr.reset()代替;(3)不能循环引用;(4)不要声明临时的shared_ptr,然后把这个指针传递给一个函数。

    Boost::scoped_ptr<T>:当一个指针离开其作用域时,释放相关资源。scoped_ptr不能共享指针的所有权也不能转移所有权,即这个内存地址只能给声明的变量用,而不能给其他使用。(1)scoped_ptr的效率和空间的消耗与内置的指针差不多;(2)scoped_ptr不能用在标准的容器上(用shared_ptr代替);(3)scoped_ptr不能指向一块能够动态增长的内存区域(用scoped_array代替)。

    Boost::shared_ptr<T>:(1)在内部维护一个引用计数器,当有一个指针指向这块内存区域时引用计数+1,反之-1,如果没有任何指针指向这块区域,引用计数器为0,释放内存区域;(2)可共享和转移所有权;(3)可被标准库的容器使用;(3)不能指向一块动态增长的内存(用shared_array代替)。

    Boost::shared_ptr<T>和Boost::scoped_ptr<T>:用于数组的内存,示例如下:

        int __tmain(int argc, _TCHAR* argv[])

        {

            const int size = 10;

            boost::shared_array<test> a(new test[size]);

            for (int i = 0; i < size; i++)

                a[i].print();    

        }

    auto_ptr: C++标准提供了一个RAII封装类auto_ptr,用于封装指向分配的堆内存(heap memory的指针,这就使得程序能够自动释放这些内存。auto_ptr类模板定义在头文件<memory>中,它的constructor接受一个指向类属类型(generic type)的指针作为参数,还重载了指针运算符*和->,以便对持有的auto_ptr对象的原始指针进行普通的指针运算。

————————————————————————————————————————————————————————————

一个智能指针示例

 1 // helloworld.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include<iostream>
6 using namespace std;
7
8 //指针类
9 class I_Pointer{
10 private:
11 int ref_count;
12 int *p;
13 I_Pointer(int *p): p(p), ref_count(1){}; //构造函数
14 ~I_Pointer(){
15 cout<<"Delete shared pointer"<<endl;
16 }
17 friend
18 class Has_Ptr;
19 };
20
21 //有指针成员的类
22 class Has_Ptr{
23 private:
24 I_Pointer *ptr; //指针成员
25 //减少引用计数
26 void release_ref_count(){
27 cout<<"release reference count"<<endl;
28 if(--(ptr->ref_count) == 1){
29 free_and_nil();
30 }
31 }
32 //增加引用计数
33 void add_ref(){
34 cout<<"add reference count"<<endl;
35 ++(ptr->ref_count);
36 }
37 //指针释放
38 void free_and_nil(){
39 delete ptr;
40 ptr = NULL;
41 }
42
43 public:
44 // 带int指针的构造函数
45 Has_Ptr(int *ptr): ptr(new I_Pointer(ptr)) {
46 add_ref();
47 }
48
49 Has_Ptr(){} //默认构造
50
51 Has_Ptr(const Has_Ptr &rhs){ //拷贝构造
52 memcpy(this, &rhs, sizeof(rhs));
53 add_ref();
54 }
55
56 ~Has_Ptr(){
57 release_ref_count();
58 }
60 //赋值
61 Has_Ptr &operator=(const Has_Ptr &rhs){
62 //原来的引用计数减1
63 release_ref_count();
64 memcpy(this, &rhs, sizeof(&rhs));
65 //新的引用计数加1
66 add_ref();
67 return *this;
68 }
70 void set_ptr(int *ptr){
71 if (ptr != (this->ptr->p)){
72 this->ptr->p = ptr;
73 }
74 }
75 int* get_ptr(){
76 return ptr->p;
77 }
78 };
79
80 void test(){
81 int i = 0;
82 Has_Ptr hp(&i);
83 int j = 1;
84 Has_Ptr hp1(&j);
85 hp1 = hp;
86 }
87 int _tmain(int argc, _TCHAR* argv[])
88 {
89 test();
90 return 0;
91 }

    当一个类的成员里面有指针的时候,使用默认拷贝构造函数的时候就会造成多个对象管理同一块内存,这样带来的后果就是如果任意一个对象释放了这一块内存,那么其他的对象再来操作这块内存的时候就会发生预料不到的结果

    为了避免对象中保存野指针从而引发的错误,提出了智能指针,它能实现多个对象共享内存的自释放。其实这种实现有点类似于delphi中的接口,到处传来传去的,最后自释放,就是因为接口有引用计数,当引用计数为1的时候就把对象释放掉

    C++中这种实现(C++ Primer书上借鉴来的),有几个比较巧妙的地方:

  • Has_Ptr这个类其实是想保存一个int *的指针,为了避免悬垂指针的出现,使用了 I_Pointer这个类把悬垂指针包了一下,因此在Has_Ptr这个类的Public接口中不会出现I_Pointer,只会出现int *;
  • 事实上也不能出现I_Pointer, 因为I_Pointer的构造函数定义成Private,不允许在外面构造,只能在friend类中构造,I_Pointer是专门为Has_Ptr实现的;
  • 要记住三元组(拷贝构造,赋值操作,析构),当其中一个需要有特殊操作的时候,其他的也需要有特殊操作,这个要形成定势思维;
posted on 2011-10-25 16:34  白草黒尖  阅读(510)  评论(0编辑  收藏  举报