无锡网站制作,排版好看的网站界面,郑州最新发布,学院网站建设的作用http://blog.csdn.net/dawn_sf/article/details/70168930 智能指针 ____________________________________________________ 今天我们来看一个高大上的东西#xff0c;它叫智能指针。 哇这个名字听起来都智能的不得了#xff0c;其实等你了解它你一定会有一点失望的。。。。因…http://blog.csdn.net/dawn_sf/article/details/70168930 智能指针 ____________________________________________________ 今天我们来看一个高大上的东西它叫智能指针。 哇这个名字听起来都智能的不得了其实等你了解它你一定会有一点失望的。。。。因为它说白了就是个管理资源的。智能指针的原理就是管理资源的RALL机制我们先来简单了解一下RALL机制RALL机制便是通过利用对象的自动销毁使得资源也具有了生命周期有了自动销毁自动回收的功能。RAII全称为Resource Acquisition Is Initialization它是在一些面向对象语言中的一种惯用法。RAII源于C在JavaC#DAdaVala和Rust中也有应用。资源分配即初始化定义一个类来封装资源的分配和释放在构造函数完成资源的分配和初始化在析构函数完成资源的清理可以保证资源的正确初始化和释放。RAII要求资源的有效期与持有资源的对象的生命期严格绑定即由对象的构造函数完成资源的分配(获取)同时由析构函数完成资源的释放。在这种要求下只要对象能正确地析构就不会出现资源泄露问题。RALL在这里就是简单提一下而已现在我们来看我们今天的主角智能指针。智能指针smart pointer是存储指向动态分配堆对象指针的类。它的诞生理由就是为粗心和懒的人设计的但是这个设计一定不是反人类的因为无论你有多厉害只要你是人你总会有犯错误的时候所以智能指针可以很好地帮助我们程序员每次 new 出来的内存都要手动 delete。程序员忘记 delete流程太复杂最终导致没有 delete异常导致程序过早退出没有执行 delete 的情况并不罕见。其实智能指针只是怕你忘了delete而专门设置出来的一个对象。有没有感觉它顿时不够智能呢但是你绝对不能否认它的实用性和重要性。现在我们来看看智能指针的使用吧:对于编译器来说智能指针实际上是一个栈对象并非指针类型在栈对象生命期即将结束时智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator-”操作符直接返回对象的引用用以操作对象。访问智能指针原来的方法则使用“.”操作符。先抛开智能指针的几个版本不说我们先来讲一下它里面的 * 和 - 是如何进行运算符重载的。下面是我定义的一个类他只是为了实现原生指针的 * 和 - 功能struct AA { int a 10; int b 20; }; templateclass T class A { public: A(T* ptr) :_ptr(ptr) {} T* operator-() { return _ptr; } T operator*() { return *_ptr; } A(AT ap) {} AT operator(AT ap) {} ~A() {delete _ptr;} protected: T* _ptr; }; int main() { Aintap1(new int); *ap1 10; AAAap2(new AA); cout *ap1 endl; cout (ap2-a) (ap2-b) endl; return 0; } 请忽略这个粗糙的A类和AA结构体我们的目的只是实现原生函数的功能那么我的功能实现了吗这里结果没有一点问题那么我们现在的注意点就应该放在这里是如何实现的智能指针的三大版本的实现 好了前面那些磨人的小妖精终于清理完了现在我们真真正正的进入主题智能指针的发展史以及它的常见的三个版本。1.管理权转移 2.简单粗暴的防拷贝 3.引用计数版本注意这里我只是实现简单的思想可能写的不是很好望大家指出帮助我改正错误。管理权转移 这个智能指针是1998应用到VS上的现在我们来实现第一个何为管理权转移呢现在我列出该思想的实现代码:templateclass T class AutoPtr { public: AutoPtr(T* ptr) :_ptr(ptr) {} T* operator-() { return _ptr; } T operator*() { return *_ptr; } AutoPtr(AutoPtrT ap) { this-_ptr ap._ptr; ap._ptr NULL; } AutoPtrT operator(AutoPtrT ap) { if (this ! ap) { delete this-_ptr; this-_ptr ap._ptr; ap._ptr NULL; } return *this; } ~AutoPtr() { cout 智能指针爸爸已经释放过空间了 endl; delete _ptr; } protected: T* _ptr; }; int main() { AutoPtrintap1(new int); *ap1 10; AutoPtrintap2(ap1); AutoPtrintap3(ap2); *ap3 20; ap2 ap3; cout *ap2 endl; return 0; } 现在我们先看看它使用普通操作时的结果如何:现在的结果真的太符合我们的预料了我们要的就是这样的结果当你还沉浸自己成功的喜悦的时候这里虽然成功实现了自动释放空间的功能还有指针的功能但是看看下面这种情况:我们把main函数内修改成这个样子:int main() { AutoPtrintap1(new int); *ap1 10; AutoPtrintap2(ap1); cout *ap1 endl; return 0; } 然后结果。。调试到这一步程序崩溃了罪魁祸首就是AutoPtrintap2(ap1)这里原因就是ap2完全的夺取了ap1的管理权。然后导致ap1无家可归访问它的时候程序就会崩溃。如果在这里调用ap2 ap1程序一样会崩溃原因还是ap1被彻彻底底的夺走一切所以这种编程思想及其不符合C思想所以它的设计思想就是有一定的缺陷。所以一般不推荐使用Autoptr智能指针。 使用了也绝对不能使用和拷贝构造。历史在发展所以我们见到接下来这种想法: 简单粗暴法防拷贝 scoped智能指针 属于 boost 库定义在 namespace boost 中包含头文件#includeboost/smart_ptr.hpp 便可以使用。scoped智能指针 跟 AutoPtr智能指针 一样可以方便的管理单个堆内存对象特别的是scoped智能指针 独享所有权避免了 AutoPtr智能指针恼人的几个问题它直接就告诉用户我不提供和拷贝构造这两个功能你别用用了我也让你编不过去。来看它的实现:templateclass T class ScopedPtr { public: ScopedPtr() {} AutoPtr(T* ptr) :_ptr(ptr) {} T* operator-() { return _ptr; } T operator*() { return *_ptr; } ~AutoPtr() { cout 智能指针爸爸已经释放过空间了 endl; delete _ptr; } protected: ScopedPtr(ScopedPtrT s); ScopedPtrT operator(ScopedPtrT s); protected: T* _ptr; }; 它的意思就是我根本不会提供拷贝构造 和 的功能他强任他强我就是这样。他确实解决上一个智能指针的问题他直接让用户不能使用这个功能这个思想确实有点反人类。。由于scoped智能指针独享所有权当我们真真需要复制智能指针时需求便满足不了了如此我们再引入一个智能指针专门用于处理复制参数传递的情况。这便是如下的shared智能指针。引用计数版本 接下来我们看最后一种也就是我们现在经常用到的shared智能指针等到智能指针发展到这一步也就很成熟了它已经几乎完美的解决所有功能因为它使用了引用计数版本当指向该片资源的*_num变成0的时候释放该资源.templateclass T class shared { public: shared(T* ptr) :_ptr(ptr) , _num(new int(1)) { } shared(const sharedT ap) :_ptr(ap._ptr) , _num(ap._num) { (*_num); } sharedT operator(const sharedT ap) { if (_ptr ! ap._ptr) { Release(); _ptr ap._ptr; _num ap._num; (*_num); } return *this; } T* operator-() { return _ptr; } T operator*() { return *_ptr; } void Release() { if (0 (--*_num)) { cout 智能指针爸爸帮你释放空间了 endl; delete _ptr; delete _num; _ptr NULL; _num NULL; } } ~shared() { Release(); } protected: T* _ptr; int* _num; }; int main() { sharedintap1(new int); *ap1 2; sharedintap2(ap1); cout *ap2 endl; sharedintap3(new int); ap3 ap1; } 上面就是我实现的简易的shared智能指针现在我们调用这个智能指针我们来看看结果我们发现它完美的解决了一切功能这个指针真的算是很完美的思想不过你再完美也会有瑕疵要不然也不会有boost::weak_ptr的存在boost::weak_ptr的存在就是为boost::shared_ptr解决一点点瑕疵的。这个问题藏得极深一般不会遇到的但是当你真的遇到的时候我相信你会绞尽脑汁的找BUG还是很难找的。话不多说现在我们来看下面这个例子struct ListNode { int _data; shared_ptrListNode _prev; shared_ptrListNode _next; ListNode(int x) :_data(x) , _prev(NULL) ,_next(NULL) {} ~ListNode() { cout ~ListNode endl; } }; int main() { shared_ptrListNode cur(new ListNode(1)); shared_ptrListNode next(new ListNode(2)); cur-_next next; next-_prev cur; cout cur cur.use_count() endl; cout next next.use_count() endl; return 0; } 现在我们验证shared智能指针的缺陷就不用我实现的那个了那个好多功能我都没实现我们用专家写的shared_ptr智能指针构造两个双向链表里面的结点这里这个双向链表可能有一点简陋但是我们只是需要它的prev和next指针就够了。现在我们运行代码看看会发生什么情况现在cur和next指针所管理的结点现在都有两个指针指针管理然后在这里会发生这样一件事:循环引用一般都会发生在这种你中有我我中有你的情况里面这里导致的问题就是内存泄漏这段空间一直都没有释放,现在很明显引用计数在这里就不是很合适了但是shared_ptr除了这里不够完善其他的地方都是非常有用的东西所以编写者在这里补充一个week_ptr接下来我们看最后一个智能指针week_ptr。week_ptr weak_ptr是为了配合shared_ptr而引入的一种智能指针它更像是shared_ptr的一个助手而不是智能指针因为它不具有普通指针的行为没有重载operator*和-,它的最大作用在于协助shared_ptr工作像旁观者那样观测资源的使用情况.通俗一点讲就是首先weak_ptr 是专门为shared_ptr 而准备的。现在我们并不能根据内部的引用计数。weak_ptr 是 boost::shared_ptr 的观察者对象观察者意味着weak_ptr 只对shared_ptr 进行引用而不改变其引用计数当被观察的shared_ptr 失效后相应的weak_ptr 也相应失效然后它就什么都不管光是个删 也就是这里的cur和next在析构的时候 不用引用计数减一 直接删除结点就好。这样也就间接地解决了循环引用的问题当然week_ptr指针的功能不是只有这一个。但是现在我们只要知道它可以解决循环引用就好。现在总结一下 1、在可以使用 boost 库的场合下拒绝使用 std::auto_ptr因为其不仅不符合 C 编程思想。 2、在确定对象无需共享的情况下使用 boost::scoped_ptr。 3、在对象需要共享的情况下使用 boost::shared_ptr。 4、在需要访问 boost::shared_ptr 对象而又不想改变其引用计数的情况下(循环引用)使用boost::weak_ptr。 5、最后一点,在你的代码中尽量不要出现 delete 关键字因为我们有智能指针。