C++中指针申请和释放内存通常采用的方式是new和delete。然而标准C++中还有一个强大的模版类就是auto_ptr,它可以在你不用的时候自动帮你释放内存。下面简单说一下用法。

  1. 用法一:
  2. std::auto_ptr<MyClass>m_example(new MyClass());
  3. 用法二:
  4. std::auto_ptr<MyClass>m_example;
  5. m_example.reset(new MyClass());
  6. 用法三(指针的赋值操作):
  7. std::auto_ptr<MyClass>m_example1(new MyClass());
  8. std::auto_ptr<MyClass>m_example2(new MyClass());
  9. m_example2=m_example1;

则C++会把m_example所指向的内存回收,使m_example1 的值为NULL,所以在C++中,应绝对避免把auto_ptr放到容器中。即应避免下列代码:

vector<auto_ptr<MyClass>>m_example;

当用算法对容器操作的时候,你很难避免STL内部对容器中的元素实现赋值传递,这样便会使容器中多个元素被置位NULL,而这不是我们想看到的。

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

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

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

  1. // 示例1(a):原始代码
  2. void f()
  3. {
  4. T* pt( new T );
  5. /*...更多的代码...*/
  6. delete pt;
  7. }

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

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

  1. // 示例1(b):安全代码,使用了auto_ptr
  2. void f()
  3. {
  4. auto_ptr<T> pt( new T );
  5. /*...更多的代码...*/
  6. } // 酷:当pt出了作用域时析构函数被调用,从而对象被自动删除

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

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

  1. // 示例2:使用一个auto_ptr
  2. void g()
  3. {
  4. // 现在,我们有了一个分配好的对象
  5. T* pt1 = new T;
  6. // 将所有权传给了一个auto_ptr对象
  7. auto_ptr<T> pt2(pt1);
  8. // 使用auto_ptr就像我们以前使用简单指针一样,
  9. *pt2 = 12;          // 就像*pt1 = 12
  10. pt2->SomeFunc(); // 就像pt1->SomeFunc();
  11. // 用get()来获得指针的值
  12. assert( pt1 == pt2.get() );
  13. // 用release()来撤销所有权
  14. T* pt3 = pt2.release();
  15. // 自己删除这个对象,因为现在没有任何auto_ptr拥有这个对象
  16. delete pt3;
  17. } // pt2不再拥有任何指针,所以不要试图删除它...OK,不要重复删除

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

  1. // 示例 3:使用reset()
  2. void h()
  3. {
  4. auto_ptr<T> pt( new T(1) );
  5. pt.reset( new T(2) );         // 删除由"new T(1)"分配出来的第一个T
  6. } // 最后pt出了作用域,第二个T也被删除了

auto_ptr用法:

1. 需要包含头文件<memory>。

2. Constructor:explicit auto_ptr(X* p = 0) throw(); 将指针p交给auto_ptr对象托管。

3. Copy constructor:auto_ptr(const auto_ptr&) throw(); template<class Y> auto_ptr(const auto_ptr<Y>& a) throw(); 指针的托管权会发生转移。

4. Destructor: ~auto_ptr(); 释放指针p指向的空间。

5. 提供了两个成员函数 X* get() const throw();       //返回保存的指针

6. 对象中仍保留指针 X* release() const throw();     //返回保存的指针,对象中不保留指针

auto_ptr实现关键点:

1. 利用特点“栈上对象在离开作用范围时会自动析构”。

2. 对于动态分配的内存,其作用范围是程序员手动控制的,这给程序员带来了方便但也不可避免疏忽造成的内存泄漏,毕竟只有编译器是最可靠的。

3. auto_ptr通过在栈上构建一个对象a,对象a中wrap了动态分配内存的指针p,所有对指针p的操作都转为对对象a的操作。而在a的析构函数中会自动释放p的空间,而该析构函数是编译器自动调用的,无需程序员操心。

多说无益,看一个最实用的例子:

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4. class TC
  5. {
  6. public:
  7. TC(){cout<<"TC()"<<endl;}
  8. ~TC(){cout<<"~TC()"<<endl;}
  9. };
  10. void foo(bool isThrow)
  11. {
  12. auto_ptr<TC> pTC(new TC);    // 方法2
  13. //TC *pTC = new TC;            // 方法1
  14. try
  15. {
  16. if(isThrow)
  17. throw "haha";
  18. }
  19. catch(const char* e)
  20. {
  21. //delete pTC;           // 方法1
  22. throw;
  23. }
  24. //delete pTC;              // 方法1
  25. }
  26. int main()
  27. {
  28. try
  29. {
  30. foo(true);
  31. }
  32. catch(...)
  33. {
  34. cout<<"caught"<<endl;
  35. }
  36. system("pause");
  37. }

1. 如果采用方案1,那么必须考虑到函数在因throw异常的时候释放所分配的内存,这样造成的结果是在每个分支处都要很小心的手动 delete pTC;。

2. 如果采用方案2,那就无需操心何时释放内存,不管foo()因何原因退出, 栈上对象pTC的析构函数都将调用,因此托管在之中的指针所指的内存必然安全释放。

至此,智能指针的优点已经很明了了。

但是要注意使用中的一个陷阱,那就是指针的托管权是会转移的。 例如在上例中,如果 auto_ptr<TC> pTC(new TC);   auto_ptr<TC> pTC1=pTC; 那么,pTC1将拥有该指针,而pTC没有了,如果再用pTC去引用,必然导致内存错误。

要避免这个问题,可以考虑使用采用了引用计数的智能指针,例如boost::shared_ptr等。auto_ptr不会降低程序的效率,但auto_ptr不适用于数组,auto_ptr根本不可以大规模使用。 shared_ptr也要配合weaked_ptr,否则会很容易触发循环引用而永远无法回收内存。 理论上,合理使用容器加智能指针,C++可以完全避免内存泄露,效率只有微不足道的下降(中型以上程序最多百分之一)。

C++ auto_ptr智能指针的用法的更多相关文章

  1. c++ auto_ptr智能指针

    c++ auto_ptr智能指针 该类型在头文件memory中,在程序的开通通过 #include<memory> 导入,接下来讲解该智能指针的作用和使用. 使用方法: auto_ptr& ...

  2. shared_ptr 和auto_ptr智能指针

    shared_ptr:计数的智能指针 它是一个包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针 ,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时 ...

  3. c++ auto_ptr 智能指针

    c++使用智能指针应该保证无论在何种情况下,只要自己被摧毁,就一定连带释放其所有资源,而由于智能型指针本身就是区域变量, 所以无论是正常退出,还是异常退出,只要函数退出,它就一定销毁 常数型auto_ ...

  4. 【VS开发】【C++开发】正确使用auto_ptr智能指针

    1, auto_ptr类 auto_ptr是一个模板类,定义如下: template <typename Type>class auto_ptr {...}: 它存储的是一个指向Type的 ...

  5. boost准模板库scoped_ptr指针的使用以及auto_ptr智能指针的对照

    首先我们看看scoped_ptr的基本使用,包括了swap(),get(),reset()的使用,重要的提醒是作用域结束的时候会自己主动析构,无需手动的释放资源: #include<boost/ ...

  6. auto_ptr智能指针

    C++的auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理. 使用std::auto_ptr,要#include <memory>.

  7. 智能指针剖析(上)std::auto_ptr与boost::scoped_ptr

    1. 引入 C++语言中的动态内存分配没有自动回收机制,动态开辟的空间需要用户自己来维护,在出函数作用域或者程序正常退出前必须释放掉. 即程序员每次 new 出来的内存都要手动 delete,否则会造 ...

  8. C++智能指针剖析(上)std::auto_ptr与boost::scoped_ptr

    1. 引入 C++语言中的动态内存分配没有自动回收机制,动态开辟的空间需要用户自己来维护,在出函数作用域或者程序正常退出前必须释放掉. 即程序员每次 new 出来的内存都要手动 delete,否则会造 ...

  9. C++智能指针 auto_ptr、shared_ptr、weak_ptr和unique_ptr

    手写代码是理解C++的最好办法,以几个例子说明C++四个智能指针的用法,转载请注明出处. 一.auto_ptr auto_ptr这是C++98标准下的智能指针,现在常常已经被C++标准的其他智能指针取 ...

随机推荐

  1. Flume(1)-概述与组成架构

    一. 定义 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统.Flume基于流式架构,灵活简单. 二. 优点 1. 可以和任意集中式存储进程集成. 2. ...

  2. django中的F和Q

    F查询 Django 提供 F() 来做这样的比较.F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值. 查询书id大于\小于价格的书籍 models.Book.ob ...

  3. Oracle笔记之——常用的函数及脚本

    一.oracle 常用的函数及关键字 1.集合操作 1)minus 差集 2)intersect 交集 3)UNION 并集,会去重 4)UNION ALL 并集,不去重2.事物 1)COMMIT ( ...

  4. Go语言基础-序言

    2018年6月,第一次接触go语言,在之后通过多本书籍渐渐了解go语言之后,开启了自己go语言全栈工程师的道路.特此记录,希望能给后学的朋友提供一个方向. 语言是一门寻寻渐进的课程,结合自己这两个月的 ...

  5. 【EXCEL】簡単に重複探し

    下記のような表があって.重複があるかどうか探すのが大変と思いますが. 簡単に重複探す方法を紹介します. Step1.重複を探す(例えこちらでは項目)を選択します. Step2.メニューで 条件付き書式 ...

  6. 20155232 2016-2017-3 《Java程序设计》第3周学习总结

    20155232 2016-2017-3 <Java程序设计>第3周学习总结 教材学习内容总结 第四章 认识对象 1.对象(Object):存在的具体实体,具有明确的状态和行为. 2.类( ...

  7. 20155316 实验四 《Android程序设计》

    实验1 实验内容 Android Stuidio的安装测试: 参考<Java和Android开发学习指南(第二版)(EPUBIT,Java for Android 2nd)>第二十四章: ...

  8. 【转载】GitHub详解

    原文:GitHub详解 GitHub详解 GitHub 是一个共享虚拟主机服务,用于存放使用Git版本控制的软件代码和内容项目.它由GitHub公司(曾称Logical Awesome)的开发者Chr ...

  9. HBase常用命令汇总——综述(一)

    hbase(main):009:0> help HBase Shell, version 1.2.4, r67592f3d062743907f8c5ae00dbbe1ae4f69e5af, Tu ...

  10. JUnit在intellij idea中只能在test里面才能使用,否则不能添加import

    只能在 src下的test下使用 不能再main下使用 否则不能import到指定的junit包 idea这样做的好处就是分离主项目和测试项目,这样一来就能够更加方便的测试了 如图直接这样把整个主包  ...