C++中的智能指针

来源:互联网 发布:java 怎么用md5 编辑:程序博客网 时间:2024/04/30 03:58

当设计含有指针的类时,应特别小心。因为指针指向的对象通常不包含在类的对象中,当类对象进行复制时,默认复制构造函数通常是浅拷贝(bitwise copy),只是拷贝了指针的值,这是两个指针指向同一个对象。通过其中一个指针就可以改变对象的值,也可以释放指针指向的对象;这时候另一个指针并不知情。引用C++ Primer的例子

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class HasPtr{  
  2. public:  
  3.         HasPtr(int *p, int i):ptr(p),val(i){}  
  4.         //获取成员变量的接口  
  5.         int *get_ptr()const { return ptr;}  
  6.         int get_int const {return val;}  
  7.         //设置成员变量的接口  
  8.         void set_per(int *p){ptr=p;}  
  9.         void set_int(int i){val=i;}  
  10.           
  11.         int get_ptr_val()const {return *ptr;}  
  12.         int set_ptr_val(int val)const {*prt=val;}//指针的值没变,所以可以为const          
  13. private:  
  14.     int *ptr;  
  15.     int val;  
  16. }  
按照如下使用:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int *ip=new int(42);  
  2. HasPtr prt1(ip,42);  
  3. HasPtr ptr2(ptr1);//bitwise copy  
这时ptr1和ptr2中的指针指向同一个对象。假如有一个对象释放,运行析构函数(如果有的话),另一个对象中的指针将成为悬浮指针,而这个对象对此毫不知情。

为了避免类似的事情发生,C++中给我们提供了智能指针。智能指针的本质是使用一个类来管理指针。在这个类里面加了一个变量--引用计数,记录与指针对象关联的类的个数。只有当引用计数为零时,才可以释放指针指向的对象。智能指针的行为要和普通指针一样。但是智能指针多了引用计数,当引用指针类的个数发现变化时(比如复制构造函数和复制操作符),引用计数要体现出这样变换。例如,在复制构造函数中,引用计数应加1。而在赋值操作符(重载)中,左操作数使用的智能指针引用计数减1 ,而右操作数使用的智能指针引用计数加1。下一个问题就是要把这个引用计数放到哪里?可以放到类HasPtr中吗?如果放到HasPtr中

class HasPtr{

……

private:

int *ptr;int val;

size_t use;//引用计数

}

这样每个对象中就会有引用计数,当我们更新引用计数时,要更新每个对象中的use值,实在难以实现。

但是真的不能放到HasPtr中吗?只要确保每个对象都能知道引用计数的值,有能确保引用计数容易更新,就可以。方法是使用指针(指针型句柄就是这样做的)。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class HasPtr{  
  2. ……        
  3. private:  
  4.     int *ptr;  
  5.     int val;  
  6.     size_t *use;//引用计数  
  7. }  


C++ Primer给我们的例子是使用计数类。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class U_Ptr{  
  2.     friend class HasPtr;  
  3.     int *ip;  
  4.     size_t use;  
  5.     U_Ptr(int *P):ip(p),use(1){}  
  6.     ~U_Ptr(){ delete ip;}  
  7. }  
HasPtr为友元。计数器类中的变量都为private。

使用智能指针类,要像使用普通指针一样,所以要重新设计HasPtr类

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class HasPtr{  
  2. public:  
  3.         HasPtr(int *p, int i):ptr(new U_Ptr(p)),val(i){}  
  4.               
  5.         //重新实现复制否造函数  
  6.         HasPtr(const HasPtr &orig):  
  7.             ptr(orig.ptr),val(orig.val){++ptr->use;}  
  8.         HasPtr&operator=(const HasPtr& rhs);  
  9.         //获取成员变量的接口  
  10.         int *get_ptr()const { return ptr->ip;}  
  11.         int get_int const {return val;}  
  12.         //设置成员变量的接口  
  13.         void set_per(int *p){ptr->ip=p;}  
  14.         void set_int(int i){val=i;}  
  15.           
  16.         int get_ptr_val()const {return *ptr->ip;}  
  17.         int set_ptr_val(int val)const {*prt->ip=val;}//指针的值没变,所以可以为const       
  18.           
  19. private:  
  20.     U_Ptr *ptr;  
  21.     int val;  
  22. }  
  23. //实现赋值操作符重载  
  24. HasPtr& HasPtr::operator=(const HasPtr &rhs)  
  25. {  
  26.     ++rhs.prt->use++;//右操作数智能指针的引用计数加1  
  27.     if(--ptr->use==0)//左操作数智能指针的引用计数减1  
  28.         delete ptr;  
  29.     ptr=rhs.ptr;  
  30.     val=rhs.val;  
  31.     return *this;  
  32. }  
0 0
原创粉丝点击