首页 > 代码库 > 智能指针原理与简单实现(转)

智能指针原理与简单实现(转)

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

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

    每次创建类的新对象时,初始化指针并将引用计数置为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;
}