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. [project X] tiny210(s5pv210)上电启动流程(BL0-BL2)

    建议参考文档: S5PV210-iROM-ApplicationNote-Preliminary-20091126 S5PV210_UM_REV1.1 项目介绍参考 [project X] tiny2 ...

  2. [Golang学习笔记] 05 程序实体2 作用域访问权限和变量重声明

    作用域访问权限: 程序实体访问权限(作用域)有三种:1. 包级私有(代码包)2. 模块级私有(代码包)3. 公开(全域). 一个函数是一个代码块.一个程序实体的作用域总是会被限制在某个代码块中.好处: ...

  3. 20155215宣言 实验四 Andoid开发基础实验报告

    20155215宣言 实验四 Andoid开发基础实验报告 实验要求 1.没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vim编辑器> 课程: 2.完成实验 ...

  4. 20155310 2016-2017-2 《Java程序设计》第四周学习总结

    20155310 2016-2017-2 <Java程序设计>第四周学习总结 一周两章新知识的自学与理解真的是很考验和锻炼我们,也对前面几章我们的学习进行了检测,遇到忘记和不懂的知识就再复 ...

  5. CF 1037 D. Valid BFS?

    D. Valid BFS? http://codeforces.com/contest/1037/problem/D 题意: 给一个序列,一棵树,判断能否bfs这棵树,得到这个序列. 分析: 将每个点 ...

  6. Apache入门 篇(二)之apache 2.2.x常用配置解析

    一.httpd 2.2.x目录结构 Cnetos 6.10 YUM安装httpd 2.2.x # yum install -y httpd 程序环境 主配置文件: /etc/httpd/conf/ht ...

  7. [Cocos2d-html5]关于压缩

    使用的是2.2版本,压缩时要注意文件顺序,main.js文件最好排在最后一个,避免错误.

  8. autoreleasepool 自动释放池的理解

    常见的面试题:以下代码存在什么样的问题?应该如何改进? for (int i = 0; i < 100000; i++) { NSString *str = @"abc"; ...

  9. git clone、git pull和git fetch的用法及区别

    声明:码字不易,转载请注明出处,欢迎文章下方讨论交流.Git 常用命令速查表 最近在一个学习小组里学习AI的课程,我们所有的学习资料和homework都放在gitlab上.今天一个小队友从gitlab ...

  10. Linux☞权限数字表示法

    权限数字表示法: 1.Linux有三种访问权限: a.可读:r(Read) b.可写:w(Write) c.可执行:x(eXcute) 2.简单说说如何去看该文件的访问权限呢?一般我们执行命令,查看目 ...