1.程序员明确的进行内存释放

对于c++程序员,最头脑的莫过于对动态分配的内存进行管理了。c++在堆上分配的内存,需要程序员负责对分配的内存进行释放。但有时内存的释放看起来并不件很轻松的事,如下程序

  1. void func()
  2. {
  3. int *p = new int(0);
  4. if(一些判断)
  5. {
  6. return;
  7. }
  8. p = new int(1);
  9. delete p;
  10. }

这个函数没有任何意义,只为说明问题。func函数至少有三处问题。1.一旦if的判断条件成立,就会立马执行返回语句。此时p所指向的内存将无法释放(这个新手一般都会犯的错误)。2. p = new int(1);语句,没有将原来分配在堆上的内存释放,造成原来那块内存永远不可能释放直到程序结束。3.这个问题并不是那么明显,假设func()函数的代码很长。

  1. void func()
  2. {
  3. int *p = new int(0);
  4. ...
  5. delete p;
  6. }

在int *p = new int(0)和delete p之间有大量代码,而这中间的代码可能有代码已经执行过delete p,并且没有把p置0。此时p为悬挂指针。后面再执行delete p就会造成运行时错误。

2.一个类似于boost shared_ptr的智能指针管理类

下面是参照c++ primer及effective c++自己写的一个智能指针管理类。使用SmartPtr,就不再需要关心内存的释放问题了。

  1. #ifndef SMARTPTR_INCLUDE_H
  2. #define SMARTPTR_INCLUDE_H
  3. #include <iostream>
  4. using namespace std;
  5. namespace commutil
  6. {
  7. template<typename T>
  8. class SmartPtr
  9. {
  10. public:
  11. SmartPtr(T *p = 0);
  12. SmartPtr(const SmartPtr &);
  13. SmartPtr &operator=(const SmartPtr &);
  14. ~SmartPtr();
  15. T &operator *();
  16. T *operator ->();
  17. private:
  18. void decreaseRef();
  19. T *m_p;
  20. int *m_useCnt;
  21. };
  22. template<typename T>
  23. SmartPtr<T>::SmartPtr(T *p=0):m_p(p)
  24. {
  25. //调用此构造函数时,默认引用数为1
  26. m_useCnt = new int(1);
  27. }
  28.  
  29. template<typename T>
  30. SmartPtr<T>::SmartPtr(const SmartPtr &rhs)
  31. {
  32. //使用复制构造函数,创建新的对象,引用数加1
  33. this->m_p = rhs.m_p;
  34. this->m_useCnt = rhs.m_useCnt;
  35. (*m_useCnt)++;
  36. }
  37.  
  38. template<typename T>
  39. SmartPtr<T>::~SmartPtr()
  40. {
  41. decreaseRef();
  42. }
  43.  
  44. template<typename T>
  45. void SmartPtr<T>::decreaseRef()
  46. {
  47. if(--(*m_useCnt)==0)
  48. {//当引用数为0时,释放heap内存
  49. delete m_useCnt;
  50. delete m_p;
  51. }
  52. }
  53.  
  54. template<typename T>
  55. T &SmartPtr<T>::operator *()
  56. {
  57. //重载解引用符
  58. return *m_p;
  59. }
  60.  
  61. template<typename T>
  62. T *SmartPtr<T>::operator ->()
  63. {
  64. //重载 ->运算符,返回真实的对象指针
  65. return m_p;
  66. }
  67.  
  68. template<typename T>
  69. SmartPtr<T> &SmartPtr<T>::operator=(const SmartPtr &anotherPtr)
  70. {
  71. if(this==&anotherPtr)
  72. {//防止自赋值
  73. return *this;
  74. }
  75. //使用赋值运算符,原来所指对象的引用数减1。
  76. decreaseRef();
  77. m_p = anotherPtr.m_p;
  78. m_useCnt = anotherPtr.m_useCnt;
  79. ++*m_useCnt;//指向新的对象,所指对象引用数加1
  80. return *this;
  81. }
  82. }
  83. #endif

3.使用SmartPtr的案例分析


3.1第一个SmartPtr的实例

  1. void func()
  2. {
  3. SmartPtr<int> autoPtr(new int(1));
  4. }

在func中,定义了SmartPtr的一个对象autoPtr,并用new int(1)在堆上分配一块内存,将分配的内存首地址传给SmartPtr的构造函数。此时autoPtr的m_useCnt的值为1。当func执行完成时,autoPtr对象超出其作用域,调用autoPtr的析构函数。在析构函数中调用私有的decreaseRef()函数,在decreaseRef()中将autoPtr的m_useCnt所内存值减1;此时m_useCnt所指内存值为0,执行delete
m_useCnt;delete m_p;。至此在创建autoPtr对象时动态分配的内存被释放。

3.2带有return语句的SmartPtr

  1. void func()
  2. {
  3. SmartPtr<int> autoPtr(new int(1));
  4. if(判断条件)
  5. {
  6. return;
  7. }
  8. }

func()函数中的if判断条件成立时,执行return;语句后,autoPtr超出其作用范围,内存释放。分析见3.1


3.3 SmartPtr对象管理新的SmartPtr对象

  1. SmartPtr<int> autoPtr1(new int(0));
  2. SmartPtr<int> autoPtr2(new int(1));
  3. autoPtr1 = autoPtr2;

执行autoPtr1 = autoPtr2,由于SmartPtr类重载了赋值运算符。实际执行的是SmartPtr的operator=()函数,在上面这个赋值语句中,先autoPtr1的m_useCnt所指内存值减1。此时m_useCnt所指内存值变为0,执行m_p所指内存释放。使用SmartPtr解决了1中所提的第二个问题。并且所有内存管理与释放工作都由SmartPtr进行,程序员不需要再关心什么时候进行内存释放。

3.4SmartPtr共享对象资源

与auto_ptr不同,SmartPtr使用引用计数机制保证多个SmartPtr对象可以管理同一个对象资源

  1. void func()
  2. {
  3. SmartPtr<int> autoPtr1(new int(0));
  4. SmartPtr<int> autoPtr2(autoPtr1);
  5. }

执行SmartPtr autoPtr2(autoPtr1)调用SmartPtr的拷贝构造函数,在拷贝构造函数中将autoPtr2的m_useCnt及m_p分别指向autoPtr1所指向的内存,再将m_useCnt的引用数加1。func()函数结束时,两次调用SmartPtr的析构函数,两次析构函数后引用数为0,删除m_useCnt,m_p所指内存。

3.5SmartPtr访问类成员函数

  1. class A
  2. {
  3. public:
  4. void print()
  5. {
  6. cout<<"A"<<endl;
  7. }
  8. };
  9.  
  10. void func()
  11. {
  12. A *obj = new A();
  13. obj->print();
  14. delete obj;
  15. }

SmartPtr重载了->运算符,可以将SmartPtr对象像指针一样访问成员函数。

  1. void func()
  2. {
  3. SmartPtr<A> autoPtr(new A());
  4. autoPtr->print();
  5. }

4.小结

SmartPtr类实现了内存对象”自管理”,使用SmartPtr程序员不再需要关心动态内存的释放问题。与shared_ptr不同,SmartPtr类只能管理指针类型的资源,并且SmartPtr不支持自定义的资源释放函数。

C++智能指针管理类的更多相关文章

  1. QT/C++插件式框架、利用智能指针管理内存空间的实现、动态加载动态库文件

    QT.C++插件式框架.主要原理还是 动态库的动态加载. dlopen()函数.下面为动态加载拿到Plugininstance对应指针.void**pp=(void**)dlsym(handle,&q ...

  2. 《C++ Primer Plus》16.2 智能指针模板类

    智能指针是行为类似于指针的类对象,单这种对象还有其他功能.本节介绍三个可帮助管理动态内存分配的智能指针类.先来看看需要哪些功能以及这些功能是如何实现的.请看下面的函数:void remodel(std ...

  3. [翻译]将智能指针用于C++的类成员

    http://stackoverflow.com/questions/15648844/using-smart-pointers-for-class-members Question: I'm hav ...

  4. C11内存管理之道:智能指针

    1.shared_ptr共享智能指针 std::shared_ptr使用引用计数,每个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr析构的时候,内存才会释放. 1.1 基本 ...

  5. C++智能指针详解

    本文出自http://mxdxm.iteye.com/ 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最 ...

  6. C++ Primer : 第十二章 : 动态内存之shared_ptr与new的结合使用、智能指针异常

    shared_ptr和new结合使用 一个shared_ptr默认初始化为一个空指针.我们也可以使用new返回的指针来初始化一个shared_ptr: shared_ptr<double> ...

  7. 【M28】智能指针

    1.什么是智能指针? 所谓智能指针就是,看起来,用起来,感觉起来都像原始指针,但是提供了更多功能. 2.使用智能指针取代原始指针,可以获得更多的控制权.如下: a.在构造和析构的时候,可以做一些事. ...

  8. 【转】C++ 智能指针详解

    一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 ...

  9. 【STL学习】智能指针之shared_ptr

    前面已经学习过auto_ptr,这里补充另外一种智能指针,比auto_ptr要更强力更通用的shared_ptr. shared_ptr 简介及使用选择  几乎所有的程序都需要某种形式的引用计数智能指 ...

随机推荐

  1. mysql之innodb_buffer_pool

    1>.mysqld重启之后,innodb_buffer_pool几乎是空的,没有任何的缓存数据.随着sql语句的执行,table中的数据以及index 逐渐被填充到buffer pool里面,之 ...

  2. G面经prepare: X-Straight

    Define “X-Straight” as X cards with consecutive numbers (X >= 3). Determine if the deck can be fu ...

  3. Leetcode: Majority Element II

    Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. The algorit ...

  4. Lintcode: Previous Permuation

    Given a list of integers, which denote a permutation. Find the previous permutation in ascending ord ...

  5. 压缩 & 解压缩 命令汇总:tar、zip & unzip、

    1. tar命令详解     格式:tar [-cxtzjvfpPN] 文件与目录 -c: 建立压缩档案 -x:解压 -t:查看内容 -r:向压缩归档文件末尾追加文件 -u:更新原压缩包中的文件 这五 ...

  6. 杭电ACM分类

    杭电ACM分类: 1001 整数求和 水题1002 C语言实验题——两个数比较 水题1003 1.2.3.4.5... 简单题1004 渊子赛马 排序+贪心的方法归并1005 Hero In Maze ...

  7. linux挂接U盘

    挂接U盘fdisk -lDisk /dev/sdd: 131 MB, 131072000 bytes/dev/sdd1 * 1 889 127983+ b Win95 FAT32#mkdir -p / ...

  8. 当执行php脚本时用户关闭浏览器会发生什么?

    2008年8月16日 1,152 views 发表评论 阅读评论 如果一段php脚本执行插入数据到mysql的操作. 一般情况下,由于php脚本在服务器上执行,此时用户虽然关闭了浏览器,但是服务器端的 ...

  9. yii2细节设置

    1.设置默认的跳转登陆页面.默认的登陆成功页 在项目的(backend/frontend的config中的main.php中的user组件中),添加loginUrl=>'admin/login' ...

  10. SqlSever中Index Seek的匹配规则(一)

    我们知道在SqlServer中,索引对查询语句的优化起着巨大的作用,一般来说在执行计划中出现了Index Seek的步骤,我们就认为索引命中了.但是Index Seek中有两个部分是值得我们注意的,我 ...