自己做的网站怎么接入银联支付,建立网站有哪些步骤?,怎么在传奇网站上做宣传,wordpress显示文件大小转载#xff1a;http://www.cnblogs.com/wuchanming/p/4411878.html 1. 介绍 本文介绍智能指针的使用。智能指针是c 中管理资源的一种方式#xff0c;用智能指针管理资源#xff0c;不必担心资源泄露#xff0c;将c 程序员 从指针和内存管理中解脱出来#xff0c;再者http://www.cnblogs.com/wuchanming/p/4411878.html 1. 介绍 本文介绍智能指针的使用。智能指针是c 中管理资源的一种方式用智能指针管理资源不必担心资源泄露将c 程序员 从指针和内存管理中解脱出来再者这也是c发展的趋势(这话不是我说的见《Effective c》和《c实践编程》)应该认真学习一下。 智能指针中最有名的应该数auto_ptr该智能指针已经被纳入标准库只需要包含memory头文件即可以使用另外TR1文档定义的shared_ptr和weak_ptr也已经实现(我用的gcc版本是gcc 4.6.1)它们的头文件是tr1/memory 。除此之外还有几个好用的智能指针不过它们属于boost库不属于STL 所以用不用得到根据自己的需要。不过了解一下总无妨正所谓功不唐捐嘛。 下面分别介绍auto_ptrscoped_ptrscoped_arrayshared_ptrshared_array weak_ptr 和 intrusive_ptr 。 2. auto_ptr 2.1 为什么要用智能指针 在介绍第一个智能指针之前先介绍下为什么要使用智能指针。先看下面这个函数
void f()
{ classA* ptr new classA; // create an object explicitly ... // perform some operations delete ptr; // clean up(destory the object explicitly) } 这个函数是一系列麻烦的根源一个显而易见的问题是我们经常忘了delete 动作特别是当函数中间存在return 语句时更是如此。然而真正的麻烦发生于更隐晦之处那就是当异常发生时我们所要面对的灾难异常一旦出现函数将立刻退离根本不会调用函数尾端的delete 语句。结果可能是内存遗失。防止这种资源遗失的常见办法就是捕捉所有异常例如
void f()
{ classA* ptr new classA; // create an object explicitly try{ ... // perform some operations } catch(...){ delete ptr; //-clean up throw;//-rethrow the exception } delete ptr; // clean up(destory the object explicitly) } 你看为了异常发生时处理对象的删除工作程序代码变得多么复杂和累赘如果还有第二个对象如果还需要更多的catch 子句那么简直是一场恶梦。这不是优良的编程风格复杂而且容易出错必须尽力避免。 如果使用智能指针情况就会大不相同了。这个智能指针应该保证无论在何种情形下只要自己被摧毁就一定要连带释放其所指资源。而由于智能型指针本身就是局部变量所以无论是正常退出还是异常退出只要函数退出它就一定会被销毁。auto_ptr正是这种只能型指针。 2.2 auto_ptr auto_ptr 是这样一种指针它是它所指向的对象的拥有者所以当身为对象拥有者的auto_ptr 被摧毁时该对象也将遭到摧毁。auto_ptr 要求一个对象只能有一个拥有者严禁一物二主。更详细的说 auto_ptr 管理的资源必须绝对没有一个以上的auto_ptr 同时指向它。 这是因为资源的拥有者在销毁的时候会销毁它所拥有的资源资源不能释放两次如果同时有两个auto_ptr拥有同一个资源那么在第一个auto_ptr销毁以后第二个auto_ptr就成为野指针了所以任何时刻一个资源只有一个拥有者。 下面是上例改写后的版本
#include iostream
#include memory using namespace std; void f() { //create and initialize an auto_ptr std::auto_ptrclassA ptr(new classA); ... //perform some operations } 不需要delete 也不再需要catch 了。auto_ptr 的接口与一般指针非常相似operator *用来提取其所指对象operator- 用来指向对象中的成员。然而所有指针算法包括在内都没有定义。 注意auto_ptr 允许你使用一般指针惯用的赋值(assign)初始化方式。必须直接使用数值来完成除始化
std::auto_ptrclassA ptr1(new classA); //OK std::auto_ptrclassA ptr2 new classA;//ERROR 有了上面两条语句那么下面的问题就很显然了。
std::auto_ptrclassA ptr; // create an auto_ptr ptr new classA; // ERROR ptr std::auto_ptrclassA(new classA); // ok, delete old object and own new 2.3 auto_ptr 拥有权的转移 上面提到过绝对没有一个以上的auto_ptr同时指向同一个资源那么如果你复制或赋值一个auto_ptr指针会发生什么呢发生拥有权转移如下所示
//initizlize an auto_ptr with a new object
std::auto_ptrclassA ptr1(new classA); //copy the auto_ptr std::auto_ptrclassA ptr2(ptr1); 在第一个语句中ptr拥有了那个new 出来的对象。在第二个语句中拥有权由ptr1 转移到ptr2,此后ptr2拥有那个对象ptr1则是一个空指针。 同理赋值动作也会发生拥有权的转移。
//initizlize an auto_ptr with a new object
std::auto_ptrclassA ptr1(new classA); //copy the auto_ptr std::auto_ptrclassA ptr2; ptr2 ptr1; 在上面的语句中如果ptr2已经拥有一个对象则赋值动作发生时会调用delete将该对象删除。 因为auto_ptr 会发生拥有权转移问题所以不能完全像使用普通指针一样使用auto_ptr 下面这个错误的用法演示的auto_ptr 的特性。
//this is a bad example
template class T void bad_print(std::auto_ptrT p)//p gets ownership of passed argument { //does p own an object? if (p.get() NULL) { std::cout NULL; } else { std::cout *p; } //Oops,exiting delete the object to which p refers } int main(int argc, char const* argv[]) { std::auto_ptrint p(new int); *p 42; // change value to which p refers bad_print(p); // Oops,deletes the memory to which p refers *p 18; // RUNTIME ERROR return 0; } 我们只是想通过print函数打印对象的值可是却不小心把对象给销毁了这是非常低级的错误再多用几次auto_ptr 以后就不会出现这种情况了。如果我们不是通过传值而是通过传递一个引用会怎么样呢可以这么做可是你得非常小心千万别在调用的函数里面将资源的拥有权转移了。正确的用法应该声明指针常量如下所示
const std::auto_ptrintp(new int); *p 42 // change value to which p refers bad_print(p); // COMPILE-TIME ERROR *p 18; // OK 注意auto_ptr 是一个指针const auto_ptr 要表达的意思是指针常量指针不可指向其他资源但是指针所指之物可以修改而不是指向常量的指针。所以const auto_ptr 类似于 T* const p而不是指向常量的指针const T* p 。下面是一个使用auto_ptr 指针的完整示例
#include iostream
#include memory using namespace std; //define output operator for auto_ptr //-print object value or NULL templateclass T ostream operator (ostream strm, const auto_ptrT p) { //does p own an object ? if (p.get() NULL) { strm NULL; } else { strm *p; } return strm; } int main(int argc, char* argv[]) { auto_ptrint p(new int(42)); auto_ptrint q; cout after initizlization: endl; cout p : p endl; cout q : q endl; q p; cout after assigning auto pointers: endl; cout p : p endl; cout q : q endl; *q 13; p q; cout after change and reassignment endl; cout p : p endl; cout q : q endl; return 0; } 输出结果如下
after initizlization:
p : 42 q : NULL after assigning auto pointers: p : NULL q : 42 after change and reassignment p : 55 q : NULL 2.4 auto_ptr 注意事项 auto_ptr(以及后面介绍的std::tr1::shared_ptr) 在其析构函数内做delete,而不是delete[]动作那意味着在动态分配而得到的array上使用auto_ptr(或tr1::shared_ptr)是一个馊主意。但是这样的代码是可以通过编译的所以需要用户自己留心。下面的代码就会出现用new []分配资源用delete而不是delete[] 释放资源一样的问题。 std::auto_ptrstd::string aps(new std::string[10]);//资源泄漏 std::tr1::shared_ptrint spi(new int[1024]); //资源泄漏 标准容器需要元素具有可复制和可赋值的特性而复制和赋值操作会使auto_ptr 发生所有权转移所以auto_ptr 不能存放在容器中。 3. scoped_ptr 有了上面对auto_ptr 的解释理解scoped_ptr 就没有什么难度了。scoped_ptr 的名字向读者传递了明确的信息这个智能指针只能在本作用域中使用不希望被转让。 scoped_ptr 通过将拷贝构造函数和operator 函数声明为私有以此阻止智能指针的复制也就关闭了所有权转移的大门。 scoped_ptr 的用法与auto_ptr 几乎一样大多数情况下它可以与auto_ptr 相互替换它也可用从一个auto_ptr 获得指针的管理权同时auto_ptr 失去管理权 scoped_ptr 也具有与auto_ptr 同样的缺陷——不能用作容器的元素但原因不同auto_ptr 是因为它的转移语义而scoped_ptr 则是因为不支持拷贝和赋值不符合容器对元素类型的基本要求。 下面的代码演示了scoped_ptr 与auto_ptr 的区别。
auto_ptrint ap(new int(10)); // 一个auto_ptrint scoped_ptrint sp(ap); // 从auto_ptr 获得原始指针 assert(ap.get() 0); // 原auto_ptr 不再拥有指针 ap.reset(new int(20)); // auto_ptr 拥有新的指针 cout *ap , *sp endl; auto_ptrint ap2; ap2 ap; // ap2 从ap 获得原始指针发生所有权转移 assert(ap.get() 0); // ap 不再拥有指针 scoped_ptrint sp2; // 另一个scoped_ptr sp2 sp; // 赋值操作无法通过编译 比起auto_ptr scoped_ptr 更明确的表达了代码原始编写者的意图只能在定义的作用域内使用不可转让。 4. scoped_array scoped_array 与scoped_ptr 没什么区别主要区别就是用 new[] 分配资源用 delete [] 释放资源而scoped_ptr 用new 分配资源用delete 释放资源。用法如下
#include iostream
#include algorithm #include iterator #include boost/smart_ptr.hpp using namespace std; using namespace boost; int main(int argc, char* argv[]) { int *arr new int[100]; //动态分配资源 scoped_arrayint sa(arr);//用scoped_array 对象代理原始动态数组 //scoped_arrayint sa( new int[100]); fill_n(sa[0], 100, 5); sa[10] sa[20] sa[30];//像普通数组一样使用 cout sa[10] \t sa[20] endl; return 0;//在作用域最后自动释放资源 } scoped_array 与 scoped_ptr 接口和功能几乎一样主要区别如下
构造函数接受的指针p 必须是new [] 的结果而不能是new 表达式的结果。没有* - 操作符重载因为scoped_array 持有的不是一个普通指针析构函数使用delete []释放资源而不是delete提供operator[] 操作符重载可以像普通数组一样使用下标访问元素没有begin() end() 等类似容器的迭代器 上面这个例子可以很方便的使用vector代替《Boost 程序库开发指南》的作者并不推荐使用scoped_array。 5. shared_ptr 5.1 shared_ptr 介绍 上面已经介绍了3种智能指针如果按照重要程度排序auto_ptr 是最重要的其次应该算shared_ptr 了shared_ptr 已经被纳入标准库了用gcc 的用户只需要#includetr1/memory用visual studio 08/10 的用户通过加入头文件#includememory即可。 shared_ptr 是一个最像指针的智能指针它实现了引用计数的功能所以指针可以随意复制在函数间传递或者存储在容器里面。 shared_ptr 还有两个特有的成员函数分别是
unique() 用于检查指针是否唯一的如果是唯一的就相当于auto_ptruse_count() 返回当前指针的引用计数use_count() 不提供高效率的操作所以use_count() 应该仅仅用于测试或者调试。 下面看看shared_ptr 的用法
#include iostream
#include tr1/memory #include assert.h using namespace std; int main(int argc, char* argv[]) { std::tr1::shared_ptrint sp( new int(10));//一个指向整数的shared_ptr assert( sp.unique());//现在shared_ptr 是指针的唯一持有者 std::tr1::shared_ptrint sp2 sp;//第二个shared_ptr 拷贝构造函数 //两个shared_ptr 相等指向同一个对象且引用计数为2 assert(sp sp2 sp.use_count() 2); *sp2 100;//使用解引用操作符修改被指对象 assert(*sp 100);//另一个shared_ptr 同时也被修改 sp.reset(); assert(!sp);//sp 不再持有对象 return 0; } 再看一个复杂一点的例子用以演示智能指针作为成员变量和函数参数的情况。
#include iostream
#include tr1/memory #include assert.h using namespace std; using namespace std::tr1; class shared{ private: shared_ptrint p; public: shared(shared_ptrint p_):p(p_){}; void print() { cout count: p.use_count() v *p endl; } }; void print_fun(shared_ptrint p) { cout count: p.use_count() v *p endl; } int main(int argc, char* argv[]) { shared_ptrint p(new int(100)); shared s1(p), s2(p); s1.print(); s2.print(); *p 20; print_fun(p); s1.print(); } 输出结果如下
count:3 v 100 count:3 v 100 count:4 v 20 count:3 v 20 可以看到我们不用关心shared_ptr 的具体实现也不需要烦心它的引用计数是多少我们只需要把它当成一个普通指针使用再也不用担心资源泄漏。 auto_ptr 不能一物侍二主所以拷贝的时候会发生所有权转移而shared_ptr 则不存在这个问题呢那么把一个 auto_ptr 复制给 shared_ptr 或者把一个shared_ptr 复制给auto_ptr 会发生什么呢答案是编译错误即你不能这么做。 5.2 make_shared 前面说过shared_ptr 是最像指针的智能指针有了shared_ptr 我们几乎可以抛弃delete了但是我们还是用到了new,用到了new 而不delete 很不对称不是吗所以TR1又定义了一个小工具make_shared(类似与make_pair)来帮助我们生成对象不过这个功能好像还没有实现如果等不及要玩一下可以用boost库make_shared 在头文件#includeboost/make_shared.hpp中定义使用方法如下
#include iostream
#include string #include vector #include boost/make_shared.hpp using namespace std; int main(int argc, char const* argv[]) { boost::shared_ptrstring sp boost::make_sharedstring(make_shared); cout *sp endl; boost::shared_ptr vectorint spv boost::make_shared vectorint (10, 2); cout spv-size() endl; return 0; } shared_ptr 可以应用于标注库唯一需要牢记的是shared_ptr 是一个指针行为类似于普通指针知道这一点以后下面的代码就不难理解了。
#include boost/make_shared.hpp #include iostream #include vector #include boost/smart_ptr.hpp using namespace std; using namespace boost; int main(int argc, char const* argv[]) { typedef vector shared_ptrint vs; //声明一个持有shared_ptr 的标准容器类型元素被初始化为空指针 vs v(10); int i 0; for (vs::iterator pos v.begin(); pos ! v.end(); pos) { (*pos) make_sharedint(i);//使用工厂函数(make_shared)赋值 cout *(*pos) , ;//输出值 } cout endl; shared_ptrint p v[9]; *p 100; cout *v[9] endl; return 0; } 5.3 shared_ptr 的缺陷循环引用 shared_ptr 需要当心循环引用的问题不然还是会发生资源泄漏。详细信息见这里。 6. shared_array 我们知道scoped_ptr 和 scoped_array 的用法和区别以后很容易猜到shared_array 的用法了。 shared_array 类似于shared_ptr 它包装了new[] 操作符在堆上分配的动态数组同样使用引用计数机制为动态数组提供了一个代理可以在程序的生命周期里上期存在直到没有任何引用后才释放内存。 shared_array 的接口和功能几乎与shared_ptr 是相同的主要区别如下
构造函数接受指针p必须是new[] 的结果而不是new 分配的资源提供operator[] 操作符重载可以像普通数组一样用下标访问没有* - 操作符重载因为shared_array 持有的不是一个普通指针析构函数使用delete[] 释放资源而不是delete shared_array 用法的简单示例
#include iostream
#include boost/smart_ptr.hpp #include assert.h using namespace std; using namespace boost; int main(int argc, char const* argv[]) { shared_arrayint sa( new int[100]); shared_arrayint sa2 sa; sa[0] 10; cout sa.use_count() endl; cout sa[0] endl; assert( sa2[0] 10); return 0; } 7. weak_ptr 关于weak_ptr 我是知其然但不知其所以然。下面的说明和例子都来自《Boost 程序库完全开发指南》无任何更改没有理解透彻怕改错了。 weak_ptr 被设计为与shared_ptr 共同工作可以从一个shared_ptr 或者另一个weak_ptr 对象构造获得资源的观测权。但是weak_ptr 没有共享资源它的构造函数不会引起指针引用计数的增加。同样在weak_ptr 析构时也不会导致引用计数减少它只是一个静静的观察者。 使用weak_ptr 的成员函数use_count() 可以观测资源的引用计数另一个成员函数expired() 的功能等价于use_count() 0但更快表示被观测的资源已经不复存在。 weak_ptr 没有重载operator* 和 - 这是特意的因为它不共享指针不能操作资源这正是它弱的原因但它可以使用一个非常重要的成员函数lock() 从被观测的shared_ptr 获得一个可用的shared_ptr 对象从而操作资源。但当expired() ture的时候lock()函数返回一个存储空指针的shared_ptr 。 下面的代码示范了weak_ptr 的用法
shared_ptrint sp (new int(10)); // 一个shared_ptr assert(sp.use_count() 1); weak_ptrint wp(sp); // 从shared_ptr 创建weak_ptr assert(sp.use_count() 1); // weak_ptr 不影响引用计数 if (!wp.expired()) // 判断weak_ptr 观察的对象是否有效 { shared_ptrint sp2 wp.lock();//获得一个shared_ptr *sp2 100; assert(sp.use_count() 2); }//退出作用域sp2 自动析构引用计数减1 assert(sp.use_count() 1); sp.reset(); // shared_ptr 失效 assert(wp.expired()); // weak_ptr 将获得一个空指针 assert(!wp.lock()); 8. intrusive_ptr Boost 库实现了该指针Boost 库不推荐使用intrusive_ptr。 9. 注意事项 在资源管理类中提供对原始资源的访问(Item 15) 使用智能指针的时候可以通过get()成员函数获取原始指针从而与一下需要用到原始资源的API打交道如果这样的API特别多每次都写.get() 不光费时而且不够清晰这时应该提供隐式类型转换。如下所示 class Font{ public: .... //隐式类型转换 operator FondHandle() const {return f;} ... }; 以独立的语句将newd 对象置入智能指针(Item 17) 应该用独立的语句将newd 的对象置入只能指针考虑如下调用 processWidget(std::tr1::shared_ptr(new Widget), priority()); 在上面的调用中需要处理以下三件事
调用priority执行new Widget调用tr1::shared_ptr构造函数 c 编译器会以什么样的次序完成上面三件事我们不得而知如果调用序列如下
执行new Widget调用priority调用tr1::shared_ptr构造函数 万一对priority 的调用导致异常则new Widget返回的指针就会遗失我们无法使用也无法释放该资源所以安全的处理方式应该是这样的
std::tr1::shared_ptrWidget pw(new Widget); processWidget(pw, priority()); 10. 总结 在上面所有介绍的智能指针中auto_ptr shared_ptr 和weak_ptr 已经纳入标准库可以放心使用而不用担心可移植性的问题。其中auto_ptr 和shared_ptr 最为重要shared_ptr和普通指针最为相似不知道该用哪种类型的智能指针的时候就用shared_ptr 。 参考资料
《C 标准程序库》《Boost 程序库完全开发指南》《Effective c》 转载http://mingxinglai.com/cn/2013/01/smart-ptr/