曲靖市建设局网站,品牌营销咨询公司是做什么的,wordpress插件 投票,优惠券网站要怎么做推广转自 http://www.cppblog.com/SmartPtr/archive/2007/07/05/27549.htmlauto_ptr解析auto_ptr是当前C标准库中提供的一种智能指针#xff0c;或许相对于boost库提供的一系列眼花缭乱的智能指针#xff0c; 或许相对于Loki中那个无所不包的智能指针#xff0c;这个不怎么智能… 转自 http://www.cppblog.com/SmartPtr/archive/2007/07/05/27549.htmlauto_ptr解析auto_ptr是当前C标准库中提供的一种智能指针或许相对于boost库提供的一系列眼花缭乱的智能指针 或许相对于Loki中那个无所不包的智能指针这个不怎么智能的智能指针难免会黯然失色。诚然auto_ptr有这样那样的不如人意以至于程序员必须像使用”裸“指针那样非常小心的使用它才能保证不出错以至于它甚至无法适用于同是标准库中的那么多的容器和一些算法但即使如此我们仍然不能否认这个小小的auto_ptr所蕴含的价值与理念。 auto_ptr的出现主要是为了解决“被异常抛出时发生资源泄漏”的问题。即如果我们让资源在局部对象构造时分配在局部对象析构时释放。这样即使在函数执行过程时发生异常退出也会因为异常能保证局部对象被析构从而保证资源被释放。auto_ptr就是基于这个理念而设计 这最早出现在C之父Bjarne Stroustrup的两本巨著TCPL和DE中,其主题为resource acquisition is initialization(raii,资源获取即初始化)然后又在Scott Meyer的More Effective C中相关章节的推动下被加入了C标准库。 下面我就列出auto_ptr的源代码并详细讲解每一部分。因为标准库中的代码要考虑不同编译器支持标准的不同而插入了不少预编译判断而且命名可读性不是很强即使是侯捷老师推荐的SGI版本的stl,可读性也不尽如人意 这里我用了Nicolai M. Josuttis(The C standard library作者)写的一个auto_ptr的版本并做了少许格式上的修改以易于分析阅读。 namespace std{ templateclass T class auto_ptr { private: T* ap; public: // constructor destructor ----------------------------------- (1) explicit auto_ptr (T* ptr 0) throw() : ap(ptr){} ~auto_ptr() throw() { delete ap; } // Copy assignment --------------------------------------------(2) auto_ptr (auto_ptr rhs) throw() :ap(rhs.release()) {} templateclass Y auto_ptr (auto_ptrY rhs) throw() : ap(rhs.release()) { } auto_ptr operator (auto_ptr rhs) throw() { reset(rhs.release()); return *this; } templateclass Y auto_ptr operator (auto_ptrY rhs) throw() { reset(rhs.release()); return *this; } // Dereference----------------------------------------------------(3) T operator*() const throw() { return *ap; } T* operator-() const throw() { return ap; } // Helper functions------------------------------------------------(4) // value access T* get() const throw() { return ap; } // release ownership T* release() throw() { T* tmp(ap); ap 0; return tmp; } // reset value void reset (T* ptr0) throw() { if (ap ! ptr) { delete ap; ap ptr; } } // Special conversions-----------------------------------------------(5) templateclass Y struct auto_ptr_ref { Y* yp; auto_ptr_ref (Y* rhs) : yp(rhs) {} }; auto_ptr(auto_ptr_refT rhs) throw() : ap(rhs.yp) { } auto_ptr operator (auto_ptr_refT rhs) throw() { reset(rhs.yp); return *this; } templateclass Y operator auto_ptr_refY() throw() { return auto_ptr_refY(release()); } templateclass Y operator auto_ptrY() throw() { return auto_ptrY(release()); } };}1 构造函数与析构函数auto_ptr在构造时获取对某个对象的所有权(ownership),在析构时释放该对象。我们可以这样使用auto_ptr来提高代码安全性int* p new int(0);auto_ptrint ap(p);从此我们不必关心应该何时释放p 也不用担心发生异常会有内存泄漏。这里我们有几点要注意1) 因为auto_ptr析构的时候肯定会删除他所拥有的那个对象所有我们就要注意了一个萝卜一个坑两个auto_ptr不能同时拥有同一个对象。像这样int* p new int(0);auto_ptrint ap1(p);auto_ptrint ap2(p);因为ap1与ap2都认为指针p是归它管的在析构时都试图删除p, 两次删除同一个对象的行为在C标准中是未定义的。所以我们必须防止这样使用auto_ptr.2) 考虑下面这种用法int* pa new int[10];auto_ptrint ap(pa);因为auto_ptr的析构函数中删除指针用的是delete,而不是delete [],所以我们不应该用auto_ptr来管理一个数组指针。3) 构造函数的explicit关键词有效阻止从一个“裸”指针隐式转换成auto_ptr类型。4) 因为C保证删除一个空指针是安全的 所以我们没有必要把析构函数写成~auto_ptr() throw() { if(ap) delete ap;} 2 拷贝构造与赋值与引用计数型智能指针不同的auto_ptr要求其对“裸”指针的完全占有性。也就是说一个”裸“指针不能同时被两个以上的auto_ptr所拥有。那么在拷贝构造或赋值操作时我们必须作特殊的处理来保证这个特性。auto_ptr的做法是“所有权转移”即拷贝或赋值的源对象将失去对“裸”指针的所有权所以与一般拷贝构造函数赋值函数不同 auto_ptr的拷贝构造函数赋值函数的参数为引用而不是常引用(const reference).当然一个auto_ptr也不能同时拥有两个以上的“裸”指针所以拷贝或赋值的目标对象将先释放其原来所拥有的对象。这里的注意点是1) 因为一个auto_ptr被拷贝或被赋值后 其已经失去对原对象的所有权这个时候对这个auto_ptr的提领(dereference)操作是不安全的。如下:int* p new int(0);auto_ptrint ap1(p);auto_ptrint ap2 ap1;cout*ap1; //错误此时ap1只剩一个null指针在手了这种情况较为隐蔽的情形出现在将auto_ptr作为函数参数按值传递因为在函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的auto_ptr(拷贝构造)这样传入的实参auto_ptr就失去了其对原对象的所有权而该对象会在函数退出时被局部auto_ptr删除。如下void f(auto_ptrint ap){cout*ap;}auto_ptrint ap1(new int(0));f(ap1);cout*ap1; //错误经过f(ap1)函数调用ap1已经不再拥有任何对象了。因为这种情况太隐蔽太容易出错了 所以auto_ptr作为函数参数按值传递是一定要避免的。或许大家会想到用auto_ptr的指针或引用作为函数参数或许可以但是仔细想想我们并不知道在函数中对传入的auto_ptr做了什么 如果当中某些操作使其失去了对对象的所有权 那么这还是可能会导致致命的执行期错误。 也许用const reference的形式来传递auto_ptr会是一个不错的选择。 2)我们可以看到拷贝构造函数与赋值函数都提供了一个成员模板在不覆盖“正统”版本的情况下实现auto_ptr的隐式转换。如我们有以下两个类class base{};class derived: public base{};那么下列代码就可以通过实现从auto_ptrderived到auto_ptrbase的隐式转换因为derived*可以转换成base*类型auto_ptrbase apbase auto_ptrderived(new derived); 3) 因为auto_ptr不具有值语义(value semantic), 所以auto_ptr不能被用在stl标准容器中。所谓值语义是指符合以下条件的类型(假设有类A)A a1;A a2(a1);A a3;a3 a1;那么a2 a1, a3 a1很明显auto_ptr不符合上述条件而我们知道stl标准容器要用到大量的拷贝赋值操作并且假设其操作的类型必须符合以上条件。3 提领操作(dereference)提领操作有两个操作 一个是返回其所拥有的对象的引用 另一个是则实现了通过auto_ptr调用其所拥有的对象的成员。如struct A{ void f();}auto_ptrA apa(new A);(*apa).f();apa-f();当然 我们首先要确保这个智能指针确实拥有某个对象否则这个操作的行为即对空指针的提领是未定义的。 4 辅助函数1) get用来显式的返回auto_ptr所拥有的对象指针。我们可以发现标准库提供的auto_ptr既不提供从“裸”指针到auto_ptr的隐式转换(构造函数为explicit),也不提供从auto_ptr到“裸”指针的隐式转换从使用上来讲可能不那么的灵活 考虑到其所带来的安全性还是值得的。2) release,用来转移所有权3) reset用来接收所有权如果接收所有权的auto_ptr如果已经拥有某对象 必须先释放该对象。 5 特殊转换这里提供一个辅助类auto_ptr_ref来做特殊的转换按照标准的解释 这个类及下面4个函数的作用是使我们得以拷贝和赋值non-const auto_ptrs 却不能拷贝和赋值const auto_ptrs. 我无法非常准确的理解这两句话的意义但根据我们观察与试验应该可以这样去理解没有这些代码我们本来就可以拷贝和赋值non-const的auto_ptr和禁止拷贝和赋值const的auto_ptr的功能 只是无法拷贝和赋值临时的auto_ptr(右值) 而这些辅助代码提供某些转换使我们可以拷贝和赋值临时的auto_ptr但并没有使const的auto_ptr也能被拷贝和赋值。如下auto_ptrint ap1 auto_ptrint(new int(0));auto_ptrint(new int(0))是一个临时对象一个右值一般的拷贝构造函数当然能拷贝右值因为其参数类别必须为一个const reference, 但是我们知道auto_ptr的拷贝函数其参数类型为reference所以为了使这行代码能通过我们引入auto_ptr_ref来实现从右值向左值的转换。其过程为1) ap1要通过拷贝 auto_ptrint(new int(0))来构造自己2) auto_ptrint(new int(0))作为右值与现有的两个拷贝构造函数参数类型都无法匹配也无法转换成该种参数类型3) 发现辅助的拷贝构造函数auto_ptr(auto_ptr_refT rhs) throw()4) 试图将auto_ptrint(new int(0))转换成auto_ptr_refT5) 发现类型转换函数operator auto_ptr_refY() throw() 转换成功从而拷贝成功。从而通过一个间接类成功的实现了拷贝构造右值临时对象同时这个辅助方法不会使const auto_ptr被拷贝 原因是在第5步 此类型转换函数为non-const的我们知道const对象是无法调用non-const成员的 所以转换失败。当然 这里有一个问题要注意 假设你把这些辅助转换的代码注释掉该行代码还是可能成功编译这是为什么呢debug一下 我们可以发现只调用了一次构造函数而拷贝构造函数并没有被调用原因在于编译器将代码优化掉了。这种类型优化叫做returned value optimization它可以有效防止一些无意义的临时对象的构造。当然前提是你的编译器要支持returned value optimization。 可见auto_ptr短短百来行的代码还是包含了不少玄机的。 转载于:https://www.cnblogs.com/XiHua/p/3711025.html