智能指针的实现机理

  • 介绍

  智能指针是用来实现指针指向的对象的共享的。其实现的基本思想:
  1.每次创建类的新对象时,初始化指针并将引用计数置为1;
  2.当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
  3.对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数减至0,则删除对象),并增加右操作数所指对象的引用计数;
  4.调用析构函数时,减少引用计数(如果引用计数减至0,则删除基础对象);
  4.重载“->”以及“*”操作符,使得智能指针有类似于普通指针的操作。
  根据以上分析,首先可以得出下面的类模板原型。

template <class T>
class SmartPointer
{
public:

    SmartPointer(T *p = 0);//构造函数
    SmartPointer(const SmartPointer& src);//拷贝构造函数
    SmartPointer& operator = (const SmartPointer& rhs);//赋值函数

    T* operator -> ();//重载->
    T& operator * ();//重载*
    ~SmartPointer();//析构函数

private:

    void MinusRef()
    {
        //被其他成员函数所调用
        if (--*m_pRef == 0)//自身的引用计数减1
        {
            //如果计数为0,则释放内存
            delete m_ptr;

            delete m_pRef;
        }

    }

    T *m_ptr;                           //保存对象指针
    size_t *m_pRef;                     //保存引用计数
};

  上面的私有成员函数MinusRef将引用计数减1。如果引用计数减至0,则删除m_ptr所指对象。根据前面的分析,MinusRef只被赋值函数以及析构函数使用。
下面说明各个成员的具体定义。
  首先是构造函数与析构函数的定义。普通构造函数中,m_ptr与p指向同一块内存,并初始化引用计数为1。拷贝构造函数中与普通构造函数的不同之处为引用计数需要加1。析构函数调用私有成员MinusRef对引用计数递减,并且判断是否需要释放对象。代码如下。

template<class T>
SmartPointer<T>::SmartPointer(T *p)     //普通构造函数
{
    m_ptr = p;                          //m_ptr与p指向同一内存
    m_pRef = new size_t(1);             //m_pRef初值为1
}

template<class T>
SmartPointer<T>::SmartPointer(const SmartPointer<T>& src)//拷贝构造函数
{
    m_ptr = src.m_ptr;      //m_ptr与src.m_ptr指向同一内存
    m_pRef++;
    m_pRef = src.m_pRef;    //拷贝引用计数

}

template<class T>
SmartPointer<T>::~SmartPointer()       //析构函数
{
    MinusRef();             //引用减1,如果减后的引用为0,则释放内存
    std::cout<<"SmartPointer: Destructor"<<std::endl;
}

  接下来是“->”和“*”的重载。这两个函数很简单,只需要分别返回m_ptr以及m_ptr所指的内容即可。注意,如果m_ptr此时为空,则应该抛出异常。代码如下。


template<class T>
T* SmartPointer<T>::operator -> ()  //重载 ->
{
    if (m_ptr)
        return m_ptr;

    //m_ptr为NULL时,抛出异常

    throw std::runtime_error("access through NULL pointer");
}
template<class T>
T& SmartPointer<T>::operator * () //重载 *
{
    if (m_ptr)
        return *m_ptr;
    //m_ptr为NULL时,抛出异常

    throw std::runtime_error("dereference of NULL pointer");
}

  最后是赋值函数的实现:

template<class T>

SmartPointer<T>& SmartPointer<T>::operator = (const SmartPointer<T>& rhs)//赋值函数
{
    ++*rhs.m_pRef;      //rhs的引用加1
    MinusRef();         //自身指向的原指针的引用减1
    m_ptr = rhs.m_ptr;  //m_ptr合rhs.m_ptr指向同一个对象
    m_pRef = rhs.m_pRef; //复制引用
    return *this;
}

  这样,就可以像 std::auto_ptr那样来使用SmartPointer。以下先定义一个测试类,测试程序如下。

class Test
{
public:
    Test() {name = NULL;}
    Test(const char* strname)//构造函数
    {
        name = new char[strlen(strname)+1];//分配内存
        strcpy(name, strname);//拷贝字符串
    }
    Test& operator = (const char *namestr)//赋值函数
    {
        if (name != NULL)
        {
            delete name;//释放原来的内存
        }
        name = new char[strlen(namestr)+1];//分配新内存
        strcpy(name, namestr);//拷贝字符串
        return *this;
    }
    void ShowName() {cout << name << endl;}
    ~Test()
    {
        if (name != NULL)
        {
            delete name;
        }
        name = NULL;
        cout << "delete name" << endl;
    }
public:
    char *name;
};

int _tmain(int argc, _TCHAR* argv[])
{
    SmartPointer<Test> t1;//空指针
    SmartPointer<Test> t2(new Test("fefd"));
    SmartPointer<Test> t3(new Test("wewew"));
    try
    {
        t1->ShowName();//空指针调用抛出异常
    }
    catch (const exception& err)
    {
        cout << err.what() << endl;
    }

    t2->ShowName();     //使用t2调用showName()
    *t2 = "David";      //使用t2给对象赋值
    t2->ShowName();     //使用t2调用showName()
    t2 = t3;            //赋值,原来t2的对象引用为0,发生析构
                        //而t3的对象引用加1
    cout << "End of main..." << endl;

    getchar();
    return 0;
}

  main函数代码第41行,t1指向一个NULL指针,因此调用ShowName时会出现异常(异常在重载的“->”函数中被抛出)。
  main函数代码第48~50行,使用SmartPtr对象对Test对象进行操作,其方法与使用Test对象指针的操作方法相同。
  main函数代码第51行,对t2进行赋值操作,操作完成后,t2引用的原对象发生析构(此对象没有SmartPtr对象引用了),t2和t3引用同一个对象,于是这个对象的引用计数加1。注意,这里我们并没有显示地对t2所引用的原对象进行释放操作,这就是智能指针的精髓所在。

文章已迁移到个人网站 –>《智能指针的实现机理》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值