转载:智能指针auto_ptr

很多人听说过标准auto_ptr智能指针机制,但并不是每个人都天天使用它。这真是个遗憾,因为auto_ptr优雅地解决了C++设计和编码中常见的问题,正确地使用它可以生成健壮的代码。本文阐述了如何正确运用auto_ptr来让你的代码更加安全——以及如何避免对auto_ptr危险但常见的误用,这些误用会引发间断性发作、难以诊断的bug。

1.为什么称它为“自动”指针?
auto_ptr只是众多可能的智能指针之一。许多商业库提供了更复杂的智能指针,用途广泛而令人惊异,从管理引用的数量到提供先进的代理服务。可以把标准C++ auto_ptr看作智能指针的Ford Escort(elmar注:可能指福特的一种适合家居的车型):一个简易、通用的智能指针,它不包含所有的小技巧,不像专用的或高性能的智能指针那么奢华,但是它可以很好的完成许多普遍的工作,它很适合日常性的使用。

auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。这里是一个简单的代码示例,没有使用auto_ptr所以不安全:
    // 示例 1(a): 原始代码
    //

#include<memory>

using namespace std;
    void f()
    {
      T* pt( new T );

/*...更多的代码...*/

delete pt;
    }

我们大多数人每天写类似的代码。如果f()函数只有三行并且不会有任何意外,这么做可能挺好的。但是如果f()从不执行delete语句,或者是由于过早的返回,或者是由于执行函数体时抛出了异常,那么这个被分配的对象就没有被删除,从而我们产生了一个经典的内存泄漏。

能让示例1(a)安全的简单办法是把指针封装在一个“智能的”类似于指针的对象里,这个对象拥有这个指针并且能在析构时自动删除这个指针所指的对象。因为这个智能指针可以简单的当成一个自动的对象(这就是说,它出了作用域时会自动毁灭),所以很自然的把它称之为“智能”指针:

// 示例 1(b): 安全代码, 使用了auto_ptr
    //
    void f()
    {
      auto_ptr<T> pt( new T );

/*...更多的代码...*/

} // 酷: 当pt出了作用域时析构函数被调用,
      // 从而对象被自动删除

现在代码不会泄漏T类型的对象,不管这个函数是正常退出还是抛出了异常,因为pt的析构函数总是会在出栈时被调用。清理会自动进行。

最后,使用一个auto_ptr就像使用一个内建的指针一样容易,而且如果想要“撤销”资源,重新采用手动的所有权,我们只要调用release():

// 示例 2: 使用一个 auto_ptr
    //
    void g()
    {
      T* pt1 = new T;
      // 现在,我们有了一个分配好的对象

// 将所有权传给了一个auto_ptr对象
      auto_ptr<T> pt2( pt1 );

// 使用auto_ptr就像我们以前使用简单指针一样
      *pt2 = 12;       // 就像 "*pt1 = 12;"
      pt2->SomeFunc(); // 就像 "pt1->SomeFunc();"

// 用get()来获得指针的值
      assert( pt1 == pt2.get() );

// 用release()来撤销所有权
      T* pt3 = pt2.release();

// 自己删除这个对象,因为现在
      // 没有任何auto_ptr拥有这个对象
      delete pt3;

} // pt2不再拥有任何指针,所以不要
      // 试图删除它...ok,不要重复删除

最后,我们可以使用auto_ptr的reset()函数来重置auto_ptr使之拥有另一个对象。如果这个auto_ptr已经拥有了一个对象,那么,它会先删除已经拥有的对象,因此调用reset()就如同销毁这个auto_ptr,然后新建一个并拥有一个新对象:

// 示例 3: 使用reset()
    //
    void h()
    {
      auto_ptr<T> pt( new T(1) );

pt.reset( new T(2) );
        // 删除由"new T(1)"分配出来的第一个T

} // 最后,pt出了作用域,
      // 第二个T也被删除了

转载地址: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应用与标准容器:

  1. <span style="font-size:14px;"><strong>#include <boost/make_shared.hpp>
  2. #include <vector>
  3. using namespace std;
  4. using namespace boost;
  5. int main ()
  6. {
  7. typedef vector< shared_ptr<int> > sharedContainers;
  8. sharedContainers sharedArray(10);
  9. for(sharedContainers::interator pos = sharedArray.begin() , int i=0 ;
  10. pos!=sharedArray.end();++pos)
  11. {
  12. *pos = make_shared<int>(++i);
  13. cout<<*(*pos)<<"\t";
  14. }
  15. cout<<endl;
  16. }</strong></span>

    注意上面的代码中的头文件#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的赋值和复制操作:(继续上面的例子)

  1. <span style="font-size:14px;"><strong>#include <boost/make_shared.hpp>
  2. #include <vector>
  3. using namespace std;
  4. using namespace boost;
  5. int main (int argc, const char * argv[])
  6. {
  7. typedef vector< shared_ptr<int> > sharedContainers;
  8. sharedContainers sharedArray(10);
  9. int i=0;
  10. for(sharedContainers::iterator pos = sharedArray.begin() ;
  11. pos!=sharedArray.end();++pos)
  12. {
  13. *pos = make_shared<int>(++i);
  14. }
  15. cout<<"sharedArray[5]的初始值:"<<*sharedArray[5]<<endl;
  16. cout<<"sharedArray[5]的初始引用计数为:"<<sharedArray[5].use_count()<<endl;
  17. shared_ptr<int> p1 = sharedArray[5];
  18. *p1 = 10;
  19. cout<<"sharedArray[5]经过赋值后的值:"<<*sharedArray[5]<<endl;
  20. cout<<"sharedArray[5]赋值后的引用计数为:"<<sharedArray[5].use_count()<<endl;
  21. shared_ptr<int> p2(sharedArray[5]);
  22. cout<<"sharedArray[5]复制和赋值后的引用计数为:"<<sharedArray[5].use_count()<<endl;
  23. cout<<"sharedArray[5]的地址:
  24. "<<sharedArray[5]<<",p1的地址:"<<p1<<",p2的地址:"<<p2<<endl;
  25. *p2 = 5;
  26. cout<<"sharedArray[5]经过复制后的值:"<<*sharedArray[5]<<endl;
  27. return 0;
  28. }</strong></span>

其输出结果为:

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++哲学方面的问题需要注意,例如延时释放、包装成员函数等。

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

智能指针auto_ptr & shared_ptr的更多相关文章

  1. C++智能指针: auto_ptr, shared_ptr, unique_ptr, weak_ptr

    本文参考C++智能指针简单剖析 内存泄露 我们知道一个对象(变量)的生命周期结束的时候, 会自动释放掉其占用的内存(例如局部变量在包含它的第一个括号结束的时候自动释放掉内存) int main () ...

  2. 关于智能指针auto_ptr

    智能指针auto_ptr和shared_ptr也是面试中经常被问到的一个 感觉看auto_ptr的源码反而更加容易理解一些,因为源码的代码量并不大,而且比较容易理解. 本篇主要介绍auto_ptr 其 ...

  3. C++ | 再探智能指针(shared_ptr 与 weak_ptr)

    上篇博客我们模拟实现了 auto_ptr 智能指针,可我们说 auto_ptr 是一种有缺陷的智能指针,并且在C++11中就已经被摈弃掉了.那么本章我们就来探索 boost库和C++11中的智能指针以 ...

  4. C++智能指针(auto_ptr)详解

    智能指针(auto_ptr) 这个名字听起来很酷是不是?其实auto_ptr 只是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势,但也有其局限.本文总结的8个问题足 ...

  5. 自己动手实现智能指针auto_ptr

    面试的时候,我们经常会被问到如何自己动手实现智能指针auto_ptr.今天我就一边参考STL库中的源代码,一边将auto_ptr的实现敲一遍. auto_ptr归根到底是一个模版类,那么这个类要实现哪 ...

  6. C++ 智能指针auto_ptr

    template<class T> class auto_ptr { public: ); // Item M5 有“explicitfor”// 的描述 template<clas ...

  7. C++中的智能指针(auto_ptr)

    实际上auto_ptr 仅仅是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势.使用它不必每次都手动调用delete去释放内存.当然有利也有弊,也不是全然完美的. 本 ...

  8. 【C++】智能指针auto_ptr简单的实现

    //[C++]智能指针auto_ptr简单的实现 #include <iostream> using namespace std; template <class _Ty> c ...

  9. C++智能指针 auto_ptr

    C++智能指针 auto_ptr auto_ptr 是一个轻量级的智能指针, 定义于 memory (非memory.h)中, 命名空间为 std. auto_ptr 适合用来管理生命周期比较短或者不 ...

随机推荐

  1. Redis简介 Linux安装Redis Redis使用

    其他一些操作(包括 APPEND.GETRANGE.MSET 和 STRLENGTH 也可用于字符串.请参见http://doc.redisfans.com/string/index.html ) 使 ...

  2. 手把手带你画一个漂亮蜂窝view Android自定义view

    上一篇做了一个水波纹view  不知道大家有没有动手试试呢点击打开链接 这个效果做起来好像没什么意义,如果不加监听回调 图片就能直接替代.写这篇博客的目的是锻炼一下思维能力,以更好的面多各种自定义vi ...

  3. 应届生求职:IT博客真能当技术型职位的求职利器?

    正值一年一度的大学毕业季,面对严峻的就业形势,700万大学毕业生,不到三成的签约率,可真是苦煞了没有工作经验,也没有项目经验的应届毕业生们.拿什么打动面试官?成绩?特长?社会实践?如果这些都不能让阅读 ...

  4. How To Get Log, Trace Files In OA Framework Pages And Concurrent Request Programs

    Goal   Solution   References APPLIES TO: Oracle Supplier Lifecycle Management - Version 12.1.2 and l ...

  5. AngularJS进阶(十二)AngularJS常用知识汇总(不断更新中....)

    AngularJS常用知识汇总(不断更新中....) 注:请点击此处进行充电! app.controller('editCtrl',['$http','$location','$rootScope', ...

  6. FPGrowth 实现

    在关联规则挖掘领域最经典的算法法是Apriori,其致命的缺点是需要多次扫描事务数据库.于是人们提出了各种裁剪(prune)数据集的方法以减少I/O开支,韩嘉炜老师的FP-Tree算法就是其中非常高效 ...

  7. javase--day_01

    一.关键字: /*                            关键字:  被java语言复与特定含义的单词.      特点: 组成关键字单词的字母全部小写.   注意: A:goto和c ...

  8. OAF中的TableLayout 高级表格

    我们经常会遇到这种情况,我们要把显示界面分成几块区域来分别显示不同的内容.比如在同一行左边显示messageComponentLayout,右边显示table,这时,我们就要用到tableLayout ...

  9. 学习C++模板,初体验

    最近,看了很多码神级人物的代码,发现其代码很炫酷,尤其对模板的使用,作为小码农,感觉已经落伍了,所以应该发奋图强,好好学习和掌握模板这个东西. 模板是什么呢?有人说一个模板就是一个创建类或函数的蓝图或 ...

  10. C# DataTable,DataSet,IList,IEnumerable 互转扩展属性

    using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.R ...