c++ shared_ptr智能指针使用注意事项

       shared_ptr在boost中地位相当重要,其行为最接近原始指针,但又比指针更加安全,甚至还能提供基本的线程安全保证。它基本上解决了在使用c++开发过程中不可避免的使用指针而遇到的许多问题,常见的毫无疑问是内存泄漏和内存的提前释放,还有一些关于指针内存申请而产生的异常问题等。而要想较好的使用shared_ptr来完全取代指针绝非易事。下面简要说说使用shared_ptr需要注意的问题。

       1.share_ptr是一个类,它产生的是一个类对象,而不是一个原生的指针对象,但是为了减少类对象与针对对象使用的差异性,所以share_ptr类故意重载了两种常见的指针操作符: *和->。从而share_ptr与普通指针使用方式一样。简言之,就是share_ptr生成的一个包含类型指针容器对象,它封装了指针对象,对指针对象负全责,包括生成、释放等

      2.特别需要注意的是,share_ptr的转型函数不能使用c++常用的转型函数,即static_cast,dynamic_cast,const_cast,而要使static_pointer_cast,dynamic_pointer_cast,const_pointer_cast。原因有两个:static_cast,dynamic_cast,const_cast的功能是转换成对应的模版类型,即static_cast<T*>其实是转换成类型为T的指针;前面说了share_ptr生成的一个包含类型指针容器对象,使用简单的c++转型函数是将share_ptr对象转型为模版指针对象,这完全违背了使用share_ptr的初衷(除非你确确实实有这种需要!),导致转型的模版指针对象不能采用share_ptr进行管理。因为上面的两个原因:share_ptr为了支持转型,所以提供了类似的转型函数即static_pointer_cast<T>,从而使转型后仍然为shared_pointer对象,仍然对指针进行管理;

     3.share_ptr类重载了许多的构造函数,其中包含无参的构造函数用来创建一个持有空指针的share_ptr对象,,注意它不等价于持有void*型的指针对象。要想生成一个存储void*型的指针,可以使用share_ptr(void*)构造函数来构造,它相当于一个泛型的指针容器,拥有容纳任意类型的能力,但是将包含void*指针的shared_ptr对象使用注意2中的转型函数转型为某种类型的指针会使代码不够安全,一般不要使用;

     4.share_ptr完美支持标准容器,并且不需要担心资源泄漏。而标准容易在使用指针对象时需要特别的小心,对指针需要额外的管理。下面用代码范例来演示如何将share_ptr应用与标准容器:

#include <boost/make_shared.hpp>
#include <vector>
using namespace std;
using namespace boost;
int main ()
{
    typedef vector< shared_ptr<int> > sharedContainers;
    sharedContainers sharedArray(10);
    for(sharedContainers::interator pos = sharedArray.begin() , int i=0 ;pos!=sharedArray.end();++pos) 
     {
            *pos = make_shared<int>(++i);
            cout<<*(*pos)<<"\t";
     }
      cout<<endl;
}

    注意上面的代码中的头文件#include <boost/make_shared.hpp>,不是常见的#include <boost/smart_ptr.hpp>,这是因为make_shared.hpp中提供了一个自由工厂函数make_shared<T>(),来消除显示的new调用,其实是模仿了标准库中产生pair的函数make_pair().因为make_shared.hpp中包含了smart_ptr.hpp,所以加入了make_shared.hpp就不需要再声明smart_ptr.hpp头文件了。而smart_ptr.hpp没有包含make_shared.hpp,所以必须要单独写出make_shared.hpp

     5.share_ptr使用赋值操作符可以从另外一个share_ptr或auto_ptr获得指针的管理权,其行为等同于构造函数,但它完全不能等同于一种拷贝,只是将原来的对象的引用技术加1,说白了就是共享资源;而实际的指针赋值确是一种浅拷贝操作,关于深拷贝与浅拷贝可参与该篇文章:c++ 深拷贝与浅拷贝下面用代码范例来share_ptr的赋值和复制操作:(继续上面的例子)

#include <boost/make_shared.hpp>
#include <vector>
using namespace std;
using namespace boost;

int main (int argc, const char * argv[])
{

    typedef vector< shared_ptr<int> > sharedContainers;
    sharedContainers sharedArray(10);
    int i=0;
    for(sharedContainers::iterator pos = sharedArray.begin() ;pos!=sharedArray.end();++pos) 
    {
        *pos = make_shared<int>(++i);
    }
    cout<<"sharedArray[5]的初始值:"<<*sharedArray[5]<<endl;
    cout<<"sharedArray[5]的初始引用计数为:"<<sharedArray[5].use_count()<<endl;
    shared_ptr<int> p1 = sharedArray[5];
    *p1 = 10;
    cout<<"sharedArray[5]经过赋值后的值:"<<*sharedArray[5]<<endl;
    cout<<"sharedArray[5]赋值后的引用计数为:"<<sharedArray[5].use_count()<<endl;
    shared_ptr<int> p2(sharedArray[5]);
    cout<<"sharedArray[5]复制和赋值后的引用计数为:"<<sharedArray[5].use_count()<<endl;
    cout<<"sharedArray[5]的地址:"<<sharedArray[5]<<",p1的地址:"<<p1<<",p2的地址:"<<p2<<endl;
    *p2 = 5;
    cout<<"sharedArray[5]经过复制后的值:"<<*sharedArray[5]<<endl;
    return 0;
    
}

其输出结果为:

sharedArray[5]的初始值:6
sharedArray[5]的初始引用计数为:1
sharedArray[5]经过赋值后的值:10
sharedArray[5]赋值后的引用计数为:2
sharedArray[5]复制和赋值后的引用计数为:3
sharedArray[5]的地址:0x10010092c,p1的地址:0x10010092c,p2的地址:0x10010092c
sharedArray[5]经过复制后的值:5

可以看到,经过赋值和复制后的sharedArray[5],其引用计数增1。而且经过赋值和复制后的sharedArray[5]对象p1和p2所指向的地址完全相同。所以随便改变其中之一的值,另外两个对象指向的值也随之改变,这也是共享资源的精髓吧!

      6. share_ptr针对封装的指针要求,可以定制删除器,即使用构造函数shared_ptr(Y *p , D d)来构造shared_ptr指针。其中第一个参数是要被管理的指针,其含义与其他构造函数一样。而第二个参数,即删除器参数d则告诉shared_ptr在析构时不是使用普通的delete方法来析构指针p,而是用方法d来操作,即用d(p)来代替delete p。这里的d只需要能够像函数一样被调用,所以d既可以是一个函数对象,也可以是一个函数指针。这种删除器d比较适用于指针对象p在删除时有一定的要求,例如某中类指针,有专门的析构函数,但切记d的实参对象行为必须与delete一样,不能抛出异常,并且是可拷贝的。

      上面说了这么多,其实这只是shared_ptr的一小部分,还有很多涉及c++哲学方面的问题需要注意,例如延时释放、包装成员函数等。

      本文如有错误,欢迎指正,对此十分感谢!


  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值