c++智能指针(上)

一、有关智能指针的基本信息
(一)智能指针是RAII思想的一个产品
RAII(资源获得即初始化),定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
所谓智能指针,就是智能化(自动化)管理指针所指向的动态资源的释放。
(二)为什么要有智能指针
eg1:
void Test1()
{
    int* p=new int(1);
    if(1)
    {
        return;
    }
    delete p;    //未执行
}
eg2:
void DoSomeTing()
{
    if(1)
    {
        throw 1;
    }
}
void Test2()
{
    int* p1=new int(2);
    DoSomeTing();
    delete p1;    //未执行
}
int main()
{
    try
    {
        Test2();
    }
    catch(...)
    {
        cout<<"未知异常"<<endl;
    }
    return 0;
}
要解决此类问题,就需要使用智能指针。

二、智能指针
Boost库中的智能指针:
  1. 管理权转移(带缺陷的设计)——>auto_ptr
  2. 防拷贝(简单粗暴)——>scoped_ptr
  3. 引用计数(更实用更复杂)——>shared_ptr
C++11库中引入了unique_ptr(相当于scoped_ptr)、shared_ptr和weak_ptr。
(一)auto_ptr
先实现一个最简单的智能指针AutoPtr:
template<class T>
class AutoPtr
{
public:
    AutoPtr(T* ptr)
        :_ptr(ptr)
    {}
    ~AutoPtr()
    {
        if(_ptr)
        {
            delete _ptr;
            _ptr=NULL;
        }
    }
protected:
    T* _ptr;
};
利用该智能指针,可以将上述的eg1和eg2改正为:
void Test1()
{
    int* p=new int(1);
    AutoPtr<int> ap(p);
    if(1)
    {
        return;
    }
}

void DoSomeTing()
{
    if(1)
    {
        throw 1;
    }
}
void Test2()
{
    int* p1=new int(2);
    AutoPtr<int> ap(p1);
    DoSomeTing();
}
int main()
{
    try
    {
        Test1();
        Test2();
    }
    catch(...)
    {
        cout<<"未知异常"<<endl;
    }
    return 0;
}
但这种智能指针是存在缺陷的。
如果将main函数换为下述内容,则会使程序崩溃。
int main()
{
    AutoPtr<int> ap1(new int(1));
    AutoPtr<int> ap2(ap1);
    return 0;
}
这是因为ap1和ap2两个对象指向了同一个地方,导致该地方要被析构两次。
需要加入管理权转移的功能(加入拷贝构造函数如下):
AutoPtr(AutoPtr<T>& ap)
    :_ptr(ap._ptr)
{
    ap._ptr=NULL;
}
//此时属于浅拷贝,ap2(ap1)时,转移后ap1被置空,再使用时程序崩溃。说明此时智能指针无法完全实现指针的作用。
//至于该构造没有使用const,是因为_ptr不是const型的,因而设为const类型后无法赋值。
//另外,一般传引用时使用const是不希望改变原来的,但这里需要修改,所以不加const
//(此时使用深拷贝也不可以,因为开辟两块空间的话就是两个智能指针,而重载的目的是希望它是一个指针。)
最后,为了完整实现AutoPtr,还需要加入运算符重载函数:
AutoPtr<T>& operator=(AutoPtr<T>& ap)    //赋值运算符重载
{
      if (this != &ap)
      {
             delete _ptr;
             _ptr = ap._ptr;
             ap._ptr = NULL;
      }
      return *this;
}
T& operator*()    //解引用操作符重载
{
         return*_ptr;
}
T* operator->()    //->操作符重载(管理的是结构体的智能指针时)
{
         return_ptr;
}
Auto_ptr虽然能够通过管理权转移解决多对象指向同一地方的问题,但仍是一种有缺陷的智能指针,很多时候不允许使用。
(二)scoped_ptr
scoped_ptr的模拟实现如下:
template<class T>
class ScopedPtr
{
public:
    ScopedPtr(T* ptr)
        :_ptr(ptr)
    {}
    ~ScopedPtr()
    {
        if(_ptr)
        {
            cout<<"delete"<<_ptr<<endl;
            delete _ptr;
        }
    }
    T& operator*()
    {
        return *_ptr;
    }
    T* operator->()
    {
        return _ptr;
    }
protected:
    ScopedPtr(ScopedPtr<T>& sp);    //只声明不实现
    ScopedPtr<T>& operator=(ScopedPtr<T>& sp);    //只声明不实现
prtected:
    T* _ptr;
};
scoped_ptr为了防止拷贝,直接不允许拷贝,简单粗暴,但也不完美。
(三)shared_ptr
对于多对象指向同一地方的问题,前面已经介绍了管理权转移和防拷贝两种方法,而shared_ptr介绍了第三种解决方法——引入一个引用计数对象。
对此方法可依据下图进行理解:


shared_ptr的模拟实现如下:
template<class T>
class SharedPtr
{
public:
       SharedPtr(T* ptr)
              :_ptr(ptr)
              , _pCount(new long(1))
       {}
       ~SharedPtr()
       {
              if (--(*_pCount) == 0)
              {
                     delete _ptr;
                     delete _pCount;
              }
       }
       SharedPtr(SharedPtr<T>& sp)    //拷贝构造,需要注意使计数加1
              :_ptr(sp._ptr)
              , _pCount(sp._pCount)
       {
              ++(*_pCount);
       }
            SharedPtr<T>& operator=(SharedPtr<T>&sp)    //赋值运算符的重载
            {
                         return*this;
            }
           T& operator*()    //重载*
           {
                        return*_ptr;
           }
           T* operator->()    //重载->
           {
                        return_ptr;
           }
           longUseCount()    //返回引用计数的值,方便查看,例如:  cout<<"sp1:"<<sp1.UseCount()<<endl;
           {
                        return*_pCount;
           }
protected:
       T* _ptr;
       long* _pCount;
};
void TestSharedPtr()
{
       SharedPtr<int> sp1(new int(1));
       SharedPtr<int> sp2(sp1);
}
int main()
{
       TestSharedPtr();
       system("pause");
       return 0;
}
但对于赋值运算,其实有三种情况:

而上述的shared_ptr模拟实现代码只能解决前两种,需要对拷贝构造函数进行改进。
   SharedPtr<T>& operator=(SharedPtr<T>&sp)    //改进后的拷贝构造
       {
              if(_ptr != sp._ptr)
              {
                     if(--(*_pCount) == 0)
                     {
                           delete_ptr;
                           delete_pCount;
                     }
                     _ptr =sp._ptr;
                     _pCount =sp._pCount;
                     ++(*_pCount);
              }
              return*this;
       }
void TestSharedPtr()    //对上述三种情况进行测试,都可通过
{
       SharedPtr<int> sp1(new int(1));
       SharedPtr<int> sp2(sp1);
       SharedPtr<int> sp3(sp2);
       sp1 = sp1;
       sp1 = sp2;
       SharedPtr<int> sp4(new int(2));
       sp1 = sp4;
}









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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值