珍爱生命,远离野指针

 

Background

         估计只要是C++程序员,没有一个不痛恨这个野指针啦,而对于我们这种只能通过log来debug的程序员来说,其恨更深。

Solution

每次看到形如下面的代码时

A* p1 = new A;

A* p2 = p1;

delete p1;

我都有一种想要将p2也置成空的冲动,但往往都不遂我心愿,因为在实际中p1,p2的出现实在是神出鬼没,让你防不胜防也烦不胜烦。

鲁迅先生说过: 不在沉默中暴发就在沉默中灭亡。幸好,我没有灭亡,所以我要暴发。

在防够了,烦饱了以后,我下定决心,要端掉这个让我受尽折磨地暗堡。

         复杂问题的解决方案往往都是简单而“暴力”地,因为问题的难解决一般来说都是因为”暴力”无处着力。我坚信这个规则,所以我的思路很简单,就是要在delete p1的时候把p2也置为空。

         计算机科学中有个神话般的格言:计算机科学中的大部分问题都可以通过增加一个中间层来解决。我希望这一次神话能得以延续。

         好了,想想吧,我们要解决的问题实际上只有一个,那就是要找一个机制,让p2能知悉它所指向对象的状态(在这里是生命周期)。如果我们把A的生命周期作为一个类提出来,把它叫LifeObject吧,并给每个A的实例一个配备一个LifeObject对象,再让p1和p2指向这个生命周期对象,这样我们只须在A的构造函数中创建一个LifeObject对象,在析构函数中将告诉LifeObject,这样在使用p1和p2的时候就知道当前使用的指针是否是有效的啦。

其关系示意如下:

                                     

 

Implement

         思路有了,就像有了作战计划,那当然要开始行军了:

         那个示意图告诉了我们致少4件事:

  1. p1,p2现在不能是指向A的裸指针啦,因为一个裸指针无法自己做到从LifeObject中获取相应的信息。需要封装一下,就叫SafePtr吧;
  2. SafePtr要提供销毁A的方法,取名为Destroy;
  3. LifeObject是有引用计数的,因为它要负责在A销毁之后,通知p1,p2,…pn,同时它还必须是创建在heap上的;
  4. LifeObject中要有A的状态信息,在A构造函数中新建LifeObject时将其状态信息置为valid,在A析构时置为invalid。

最后,其结构图大致如下:

                         

从这个结构图上,我们可以看到:

  1. LifeObject是用SafeObject的一个指针m_pPointee来指示SafeObject的状态的,在SafeObject创建时把自己的地址赋给m_pPointee,在析构时将m_pPointee设为NULL;
  2. 通过将LifeObject的析构函数设为私有,并提供销毁方法Destroy,可以保证它只能在heap上创建,同时除了它的friend SafeObject以外没有人能创建它。
  3. SafePtr提供了判空函数IsNUll和NotNull;

在具体实现的时候,还要考虑的问题是SafePtr要像裸指针一样,能够从SafePtr<Derived>转化为SafePtr<Base>以及从SafePtr<Base> 安全转换到SafePtr<Derived>一些事。大家可以参见其源码

代码
 
   
1 /*
2 * SmartPointer.h
3 *
4 * Created on: 2010-9-3
5 * Author: li_shugan@126.com
6 */
7
8 #ifndef SAFEPTR_H_
9   #define SAFEPTR_H_
10 #include " SafeObject.h "
11 template < typename ReferenceType >
12   class SafePtr {
13   public :
14 explicit SafePtr(ReferenceType * pPointer);
15 // SafePtr(SafePtr& other);
16  
17 template < typename NewType >
18 SafePtr(SafePtr < NewType >& other);
19
20 // const SafePtr& operator = (SafePtr& other);
21  
22 template < typename NewType >
23 const SafePtr & operator = (SafePtr < NewType >& other);
24
25 ~ SafePtr();
26   public :
27 ReferenceType * operator -> () const ;
28 ReferenceType & operator * () const ;
29 void Destroy( void );
30 bool IsNull( void );
31 bool NotNull( void );
32
33
34 template < typename NewType >
35 operator SafePtr < NewType > ()
36 {
37 return SafePtr < NewType > (r_pValue);
38 }
39
40 template < typename NewType >
41 SafePtr < NewType > Cast()
42 {
43 return SafePtr < NewType > (dynamic_cast < NewType *> (r_pValue));
44 }
45
46   private :
47 void Init( void );
48   private :
49 ReferenceType * r_pValue;
50 LifeObject * r_pRefObj;
51
52 template < class NewType >
53 friend class SafePtr;
54 };
55 #include " SafePtr.inl "
56   #endif /* SMARTPOINTER_H_ */
57  

 

 

代码
 
   
1 /*
2 * SmartPointer.inl
3 *
4 * Created on: 2010-9-3
5 * Author: li_shugan@126.com
6 */
7 #include < cstddef >
8
9 template < typename ReferenceType >
10 SafePtr < ReferenceType > ::SafePtr(ReferenceType * pPointer):
11 r_pValue(pPointer),
12 r_pRefObj(NULL)
13 {
14 if (NULL != pPointer)
15 {
16 r_pRefObj = pPointer -> GetCountObj();
17 r_pRefObj -> AddRef();
18 }
19 }
20
21 template < typename ReferenceType >
22 SafePtr < ReferenceType > :: ~ SafePtr()
23 {
24 if (NULL != r_pRefObj)
25 {
26 r_pRefObj -> Release();
27 }
28 }
29
30   // template<typename ReferenceType>
31   // SafePtr<ReferenceType>::SafePtr(SafePtr<ReferenceType>& other):
32   // r_pValue(other.r_pValue),
33   // r_pRefObj(other.r_pRefObj)
34   // {
35   // Init();
36   // }
37  
38 template < typename ReferenceType >
39 template < typename NewType >
40 SafePtr < ReferenceType > ::SafePtr(SafePtr < NewType >& other):
41 r_pValue(other.r_pValue),
42 r_pRefObj(other.r_pRefObj)
43 {
44 Init();
45 }
46
47 template < typename ReferenceType >
48 template < typename NewType >
49 const SafePtr < ReferenceType >& SafePtr < ReferenceType > :: operator = (SafePtr < NewType >& other)
50 {
51 if (r_pRefObj != other.r_pRefObj)
52 {
53 LifeObject * oldPtr = r_pRefObj;
54
55 r_pValue = other.pPointer;
56 r_pRefObj = other.r_pRefObj;
57 Init();
58
59 if (NULL != oldPtr)
60 {
61 oldPtr -> Release();
62 }
63 }
64 return * this ;
65 }
66
67 template < typename ReferenceType >
68 void SafePtr < ReferenceType > ::Init( void )
69 {
70 if (NotNull())
71 {
72 r_pRefObj -> AddRef();
73 }
74 else
75 {
76 r_pValue = NULL;
77 r_pRefObj = NULL;
78 }
79
80 }
81
82 template < typename ReferenceType >
83 ReferenceType * SafePtr < ReferenceType > :: operator -> () const
84 {
85 return r_pValue;
86 }
87
88 template < typename ReferenceType >
89 ReferenceType & SafePtr < ReferenceType > :: operator * () const
90 {
91 return * r_pValue;
92 }
93
94 template < typename ReferenceType >
95 void SafePtr < ReferenceType > ::Destroy( void )
96 {
97 if (NULL != r_pRefObj)
98 {
99 r_pRefObj -> Destroy();
100 r_pRefObj -> Release();
101 r_pValue = NULL;
102 r_pRefObj = NULL;
103 }
104 }
105
106 template < typename ReferenceType >
107 bool SafePtr < ReferenceType > ::IsNull( void )
108 {
109 return (NULL == r_pRefObj) || ! r_pRefObj -> IsValid();
110 }
111
112 template < typename ReferenceType >
113 bool SafePtr < ReferenceType > ::NotNull( void )
114 {
115 return ! IsNull();
116 }
117

 

 

 

代码
 
   
1 /*
2 * SafeObject.h
3 *
4 * Created on: 2010-9-4
5 * Author: li_shugan@126.com
6 */
7
8 #ifndef SAFEOBJECT_H_
9 #define SAFEOBJECT_H_
10
11 class SafeObject;
12 class LifeObject
13 {
14 public :
15 explicit LifeObject(SafeObject * pPointee);
16 void Destroy( void );
17 int AddRef( void );
18 int Release( void );
19 bool IsValid( void ){ return 0 != r_pPointee;}
20 private :
21 // You should always create the instance of LifeObject in heap.
22 ~ LifeObject(){};
23
24 // can not been copy or assignment.
25 LifeObject( const LifeObject & other);
26 LifeObject & operator = ( const LifeObject & other);
27 private :
28 int m_nRefCount;
29 SafeObject * r_pPointee;
30 friend class SafeObject;
31 };
32
33 class SafeObject {
34 public :
35 SafeObject();
36 virtual ~ SafeObject() = 0 ;
37 LifeObject * GetCountObj( void ) const { return m_pLifeObject;}
38 int test(){ return 2 ;}
39 private :
40 LifeObject * m_pLifeObject;
41 };
42 #endif /* SAFEOBJECT_H_ */
43

 

 

 

代码
 
   
1 /*
2 * SafeObject.cpp
3 *
4 * Created on: 2010-9-4
5 * Author: li_shugan@126.com
6 */
7 #include < cstddef >
8 #include " SafeObject.h "
9
10 LifeObject::LifeObject(SafeObject * pPointee):
11 m_nRefCount( 0 ),
12 r_pPointee(pPointee)
13 {
14 }
15
16
17 void LifeObject::Destroy( void )
18 {
19 delete r_pPointee;
20 }
21
22
23 int LifeObject::AddRef( void )
24 {
25 return ++ m_nRefCount;
26 }
27
28
29 int LifeObject::Release( void )
30 {
31 if ( 0 == ( -- m_nRefCount))
32 {
33 delete this ;
34 }
35 return m_nRefCount;
36 }
37
38
39
40 SafeObject::SafeObject() {
41 // TODO Auto-generated constructor stub
42 m_pLifeObject = new LifeObject( this );
43 }
44
45
46 SafeObject:: ~ SafeObject() {
47 m_pLifeObject -> r_pPointee = NULL;
48 }
49

 

 

 

 

How to use it?

  1. 首先你要让你的类继承处SafeObject,如下:

class Test: public SafeObject

  1. 像使用智能指针一样使用它

     SafePtr<Test> pp(new Test);

     SafePtr<Test> pp1 = pp;

     if(pp1.NotNull())

     {

         pp1->output();

         pp1->Fun();

 

         SafePtr<SafeObject> pp3(pp);

         cout<<pp3->test()<<endl;

     }

     pp.Destroy();

     if(pp1.NotNull())

     {

         pp1->Fun();

}

  Test Code如下:

 

代码
 
   
1 /*
2 * main.cpp
3 *
4 * Created on: 2010-9-4
5 * Author: Administrator
6 */
7 #include " SafePtr.h "
8 #include " SafeObject.h "
9 #include < iostream >
10 using std::cout;
11 using std::endl;
12 class Base1
13 {
14 public :
15 void output()
16 {
17 cout << __FUNCTION__ << endl;
18 }
19 };
20 class Test: public Base1, public SafeObject
21 {
22 public :
23 ~ Test()
24 {
25 cout << __FUNCTION__ << endl;
26 }
27 public :
28 void Fun( void )
29 {
30 cout << " You invoke fun " << endl;
31 }
32 };
33 #include < memory >
34 int main( int argc, char ** argv)
35 {
36 SafePtr < Test > pp( new Test);
37 SafePtr < Test > pp1 = pp;
38 if (pp1.NotNull())
39 {
40 pp1 -> output();
41 pp1 -> Fun();
42
43 SafePtr < SafeObject > pp3(pp);
44 cout << pp3 -> test() << endl;
45 }
46 pp.Destroy();
47 if (pp1.NotNull())
48 {
49 pp1 -> Fun();
50 }
51
52 SafePtr < Test > pp4 = pp;
53 if (pp4.NotNull())
54 {
55 pp4 -> Fun();
56 }
57
58 SafePtr < Test > pp5 = pp1;
59 if (pp5.NotNull())
60 {
61 pp4 -> Fun();
62 }
63
64 return 0 ;
65 }
66

 

 

Advantage

         我想大家已经看出来了,接口的定义和智能指针很相似,只是多了一个Destroy函数而已,所以它的优点就有两个啦:

  1. 野指针在这儿灰飞烟灭了;
  2. 如果不调用destroy函数,它就是一智能指针,用它可以防止内存泄漏。

Disadvantage

  虽然它解决了C++中两大问题,但是它的缺点也是有目共睹的,除了常规智能指针的缺陷外它还多出了两个:

  1. 不能调用delete来销毁一个指针,你要通通换成对Destroy函数的的调用;
  2. 如果你想要享受特权公民的待遇,你就得让你的类从SafeObject继承一下。

转载于:https://www.cnblogs.com/li_shugan/archive/2010/09/06/1818779.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值