C++ STL 四种智能指针简单剖析

作者: 云中布衣   分类:  学习笔记    热度: (510℃)   时间: 2017-8-10 0:16   标签: #始于C++而不止于C++    

最近在看C++Primer,其中第13章复制控制有一小节专门将智能指针类的实现,但是看的有点迷糊,特在此对C++智能指针做一个简单的剖析。我们知道在C/C++程序中指针的使用非常的普遍,从普通的内置类型指针,到函数指针,再到自定义类指针,指针的存在伴随着内存分配,正常来说指针会指向系统内存的某一块空间,经过一系列的程序执行之后,原来的指针可能出现各种问题,比如程序员malloc(new)操作申请的内存,没有通过指针free(delete)得到释放,产生了内存泄露,或者释放内存空间后,原来指向该内存空间指针变成了悬空指针,成为一些危险操作的根源。先来看下面这段代码:

选区_136.png

通过上图的代码及其注释我们大致可以罗列出使用普通指针的时候,可能遇到以下问题:

1.申请的内存空间,却没有显示的释放内存代码,造成内存泄露。

2.指针指向的对象被删除或者说其指向的内存空间被释放,原指针变成了悬空指针(悬空指针不等于空指针,它会指向垃圾内存)。

3.即使显示的写了释放已申请内存空间的代码,也会因为一些异常导致释放语句没有执行,而造成内存泄露。

那么现在我们有了一种叫做智能指针的东西,它可以handle住上述所有的问题。下面标红的是摘抄自C++Primer中的原话。

简单的来说吧,智能指针是一个行为类似指针也提供其他功能的类。智能指针的一个通用形式接受指向动态分配对象的指针并负责删除该对象。用户分配对象但由智能指针类删除它。智能指针类需要实现复制控制成员来管理指向共享对象的指针。只有在撤销了指向共享对象的最后一个智能指针后,才能删除该共享对象。使用计数是实现智能指针类最常用的方式。

更简单的来说,一个类,定义了指针行为和其他功能,如:引用计数、内存管理、更全面的检查等。这种类通常定义了解引用操作符(operator*)和成员访问操作符(ooperator->)的重载版本。

看不太明白没有关系,其实我也不是很明白,上来就给我这么一出,确实很难理解,但是它说的确很对。下面我们慢慢的来剖析它。

那我们从STL中四种常见智能指针开始吧,auto_ptr、shared_ptr、unique_ptr、weak_ptr。

其中auto_ptr是C++98提供的方案,但在C++11中已经摒弃了,C++11主推后面三种方案。但由于历史原因,可能很多legacy code用的是auto_ptr,或者你的编译环境只支持auto_ptr,这是就没有办法了。后面会提高为什么要放弃auto_ptr方案而改用后面三种。

(一)智能指针初级版本:auto_ptr(官方文档

前面我们提到,申请了内存空间,每次都需要手动释放,但是程序员有可能会忘记,或者程序执行期间出现了异常,导致申请的内存空间没有得到释放,而造成内存泄露。为了解决这个问题智能指针的初级版本auto_ptr被提了出来。

它的思路很简单,将基本类型指针封装为类对象指针(这个类肯定是个模板,以适应不同基本类型的需求),并在析构函数里编写delete语句,删除指针指向的内存空间。当指针auto_ptr占据的内存被释放后,auto_ptr指向的内存也自动释放。比如这么申请内存:

std::auto_ptr<std::string> ptr(new std::string("abc"));
当栈内存中指针ptr占据内存空间被释放掉,它指向的string对象的内存也会被释放,当然你可以等程序结束自动释放,也可以使用reset()中途手动释放。其实ptr.reset()操作是重新绑定指向的对象的意思,原对象会被释放掉。

还有一个值得我们注意的成员函数release,这个函数只是把智能指针赋值为空,但是它原来指向的内存,并没有被释放掉,相当于它只是释放了对资源的所有权。如果中途使用release(),就还会造成内存泄露。解决办法应为:

std::string* tmp_ptr = ptr.release();
delete tmp_ptr;

再比如使用"="赋值语句,被赋值的auto_ptr会完全夺取右操作数的内存管理所有权,导致右边的auto_ptr指针悬空,当使用时会导致程序崩溃。也正因为如此硬避免把auto_ptr放到容器中。

(二)智能指针升级版本:shared_ptr(官方文档)、unique(官方文档

先说说shared_ptr智能指针,从名字中可以看出来资源可以被多个指针共享,它使用计数机制来表明资源当前被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造shared_ptr,其还可以通过传入auto_ptr,unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。

而unique_ptr是C++11中另一个取代auto_ptr的产物。unique_ptr是一个独享所有权的指针,它提供了严格意义上的所有权。它拥有它指向的对象,无法进行复制构造,也无法进行复制赋值操作。简单的说就是无法使两个unique_ptr指向同一个对象,但是可以进行移动构造和移动赋值操作。unique_ptr保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它所指向的对象。

(三)智能指针升级版Plus:weak_ptr(官方文档

weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数,永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获取shared_ptr。

注意我们不能通过weak_ptr直接访问对象的方法,需要先把它通过lock()方法转化为shared_ptr后才可以。

以上只是一个简单的剖析,后续还会带来,智能指针的设计与实现,深入到STL对指针实现的里面去,揭开智能指针的神秘面纱。

参考资料:

  1. 书籍:《C++ Primer 4Edition》
  2. C++ 智能指针详解:http://blog.csdn.net/xt_xiaotian/article/details/5714477
  3. c++ 智能指针用法详解:https://www.cnblogs.com/TenosDoIt/p/3456704.html

<完>

56.8K

评论:

云中布衣 Say:
@劳保用品 欢迎常来逛逛。

2017-09-13 00:52


劳保用品 Say:
谢谢分享,学习了

2017-09-12 16:02


云中布衣 Say:
@英国代写 谢谢!~

2017-09-01 23:05


英国代写 Say:
支持楼主。

2017-09-01 15:09


发表评论:

© 云中布衣 2015 | Driven by EMLOG  | SiteMap | RunTime: 8.78ms RSS  |   | 回到顶部

文章数量【230】 评论数量【156】 稳定运行【1033天】

Visitor IP Address【54.81.183.183】