zoukankan      html  css  js  c++  java
  • 智能指针原理与简单实现(转)

    以下实现没有考虑线程安全的问题。

    智能指针:它的一种通用实现方法是采用引用计数的方法。智能指针将一个计数器与类指向的对象相关联,引用计数跟踪共有多少个类对象共享同一指针。

        每次创建类的新对象时,初始化指针并将引用计数置为1;
        当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
        对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;这是因此左侧的指针指向了右侧指针所指向的对象,因此右指针所指向的对象的引用计数+1;
        调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。
        实现智能指针有两种经典策略:一是引入辅助类,二是使用句柄类。
    下面是辅助类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    class Point                                       //基础对象类,要做一个对Point类的智能指针
    {
    public:
        Point(int xVal = 0, int yVal = 0):x(xVal),y(yVal) { }
        int getX() const { return x; }
        int getY() const { return y; }
        void setX(int xVal) { x = xVal; }
        void setY(int yVal) { y = yVal; }
    private:
        int x,y;
    };
    class RefPtr                                  //辅助类
    {//该类成员访问权限全部为private,因为不想让用户直接使用该类
     friend class SmartPtr;      //定义智能指针类为友元,因为智能指针类需要直接操纵辅助类
     RefPtr(Point *ptr):p(ptr), count(1) { }
     ~RefPtr() { delete p; }
      
     int count;                                                     //引用计数
     Point *p;                                                      //基础对象指针
    };
      
    class SmartPtr                                             //智能指针类
    {
    public:
     SmartPtr(Point *ptr):rp(new RefPtr(ptr)) { }                                 //构造函数
     SmartPtr(const SmartPtr &sp):rp(sp.rp) { ++rp->count; }            //复制构造函数
     SmartPtr& operator=(const SmartPtr& rhs) {                              //重载赋值操作符
      ++rhs.rp->count;                                                         //首先将右操作数引用计数加1,
      if(--rp->count == 0)                                                                     //然后将引用计数减1,可以应对自赋值
       delete rp;
      rp = rhs.rp;
      return *this;
     }
     ~SmartPtr() {                                            //析构函数
      if(--rp->count == 0)                                  //当引用计数减为0时,删除辅助类对象指针,从而删除基础对象
       delete rp;
     }
      
    private:
     RefPtr *rp;                                                //辅助类对象指针
    };
      
    int main()
    {
     Point *p1 = new Point(10, 8);
     SmartPtr sp1(p1);    //此时sp1.rp->count = 1
     SmartPtr sp2(sp1);    //首先将sp1.rp->count赋给sp2.rp->count,之后sp2.rp->count++,这时sp1,sp2的rp是同一个对象
     Point *p2 = new Point(5, 5);
     SmartPtr sp3(p2);
     sp3 = sp1;
      
     return 0;
    }

      

     
    使用该方式的内存结构图如下:

    下面是句柄类:

    为了避免上面方案中每个使用指针的类自己去控制引用计数,可以用一个类把指针封装起来。封装好后,这个类对象可以出现在用户类使用指针的任何地方,表现为一个指针的行为。我们可以像指针一样使用它,而不用担心普通成员指针所带来的问题,我们把这样的类叫句柄类。在封装句柄类时,需要申请一个动态分配的引用计数空间,指针与引用计数分开存储。实现示例如下:
     

    #include <iostream>
    #include <stdexcept>
    using namespace std;

    #define TEST_SMARTPTR
    class Stub
    {
    public:
        void print() {
            cout<<"Stub: print"<<endl;
        }
        ~Stub(){
            cout<<"Stub: Destructor"<<endl;
        }
    };

    template <typename T>
    class SmartPtr 
    {
    public:
        SmartPtr(T *p = 0): ptr(p), pUse(new size_t(1)) { }
        SmartPtr(const SmartPtr& src): ptr(src.ptr), pUse(src.pUse) {
            ++*pUse;
        }
        SmartPtr& operator= (const SmartPtr& rhs) {
            // self-assigning is also right
            ++*rhs.pUse;
            decrUse();
            ptr = rhs.ptr;
            pUse = rhs.pUse;
            return *this;
        }
        T *operator->() {
            if (ptr)
                return ptr;
            throw std::runtime_error("access through NULL pointer");
        }
        const T *operator->() const { 
            if (ptr)
                return ptr;
            throw std::runtime_error("access through NULL pointer");
        }
        T &operator*() {
            if (ptr)
                return *ptr;
            throw std::runtime_error("dereference of NULL pointer");
        }
        const T &operator*() const {
            if (ptr)
                return *ptr;
            throw std::runtime_error("dereference of NULL pointer");
        } 
        ~SmartPtr() {
            decrUse();
    #ifdef TEST_SMARTPTR
            std::cout<<"SmartPtr: Destructor"<<std::endl; // for testing
    #endif
        }
        
    private:
        void decrUse() {
            if (--*pUse == 0) {
                delete ptr;
                delete pUse;
            }
        }
        T *ptr;
        size_t *pUse;
    };

    int main()
    {
        try {
            SmartPtr<Stub> t;
            t->print();
        } catch (const exception& err) {
            cout<<err.what()<<endl;
        }
        SmartPtr<Stub> t1(new Stub);
        SmartPtr<Stub> t2(t1);
        SmartPtr<Stub> t3(new Stub);
        t3 = t2;
        t1->print();
        (*t3).print();
        
        return 0;
    }

  • 相关阅读:
    ZEat
    BingWallpaper
    ZBreak
    C语言块内变量回收问题
    Android 隐藏RadoiButton左边按钮
    php文件管理与基础功能的实现
    Ajax写分页查询(实现不刷新页面)
    jquery写日期选择器
    ajax三级联动下拉菜单
    ajax bookstrap美化网页,并实现页面的加载,删除与查看详情
  • 原文地址:https://www.cnblogs.com/cane/p/3899531.html
Copyright © 2011-2022 走看看