zoukankan      html  css  js  c++  java
  • 珍爱生命,远离野指针

    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继承一下。
  • 相关阅读:
    delphi Int64Rec 应用实例
    PerformEraseBackground 擦除背景(ThemeServices)
    Delphi 的 Bit
    delphi 关于 "高位" 与 "低位"
    PByte和PChar的关系
    执行控制台程序并且获得它的输出结果
    Console下Pause效果的函数
    ByteType字符串中判断是否英文
    窗体包括标题作为一个位图复制到剪贴板
    inf 启动
  • 原文地址:https://www.cnblogs.com/li_shugan/p/1818779.html
Copyright © 2011-2022 走看看