C++智能指针使用总结

 时间:2024-10-19 21:08:08

C++提供了4种智能指针用于对分配的内存进行自动释放,这些智能指针如下: auto_ptr、unique_ptr、shared_ptr、weak_ptr。其中auto_ptr在C++98标准引入,后三种在C++11标准中加入。而auto_ptr已经被C++11所摒弃,建议使用后三种智能指针,这4种智能指针使用模板(template)实现。在此总结下个人对这4种智能指针肤浅认识。

工具/原料

GNU GCC g++ 4.8.1 C++编译器

C++智能指针简介

1、 C++智能指针是行为类似于指针的类对象。它使用设计模式中的代理模式,代理了原始“裸”指针的行为,为指针添加了更多更有用的特性。 C++引入异常机制后,智能指针由一种技巧升级为一种非常重要的技术,因为如果没有智能指针,程序员必须保证new对象能在正确的时机delete,四处编写异常捕获代码释放资源,而智能指针则可以在退出作用域时——不管是正常离开或是因异常离开——总调用delete来析构在堆栈上动态分配的对象。 因为C++异常处理的真正功能在于它具有为异常抛掷前构造的所有局部对象(那么智能指针对象也适用)自动调用析构函数的能力(C++异常机制不仅仅在于它能够处理各种不同类型的异常)。所以在异常退出智能指针对象作用域时,总能由C++异常机制调用析构函数释放在堆栈上动态分配的对象。 当然,正常退出对象(智能指针对象也属于此列)作用域也会自动调用析构函数释放在堆栈上动态分配的对象。 由此可知,将“裸”指针包装成智能指针对象可以实现动态分配的内存对象的自动释放。 而且C++智能指针对象可以像原指针那样直接使用运算符,如赋值运算符'=',指针运算符'->',解引用运算符'*'。这点可以从下面的”shared_ptr智能指针--shared_ptr模板类摘要“部分可以印证。

auto_ptr智能指针(C++98)

1、所属头文件:#include <memory>所属命名空间及标识符:using std::shared_ptr;所属版本:C++98g++启用版本命令:g++ -std=c++98 -c -o补充: 如果启用c++11及以上标准,即g++ -std=c++11 -c -o,编译时会有一个警告信息提示warning:‘auto_ptr’ is deprecated (‘auto_ptr‘被反对使用)

2、 存在很多种智能指针,其中最有名的应该是C++98标准中的“自动指镅氪瓷杵针”std::auto_ptr,它部分解决了获取茹霭庖褙资源自动释放的问题,例如:#include <memory>#include <iostream>#include <string>using std::cin;using std::cout;using std::string;using std::auto_ptr;class Report{private: std::string str;public: Report( const string s ):str(s) { cout << "Object created!\n"; } ~Report() { cout << "Object deleted!\n"; } void comment(string owner) const { cout << owner << str << "\n"; }};int main(void){ auto_ptr<Report> ps (new Report("Using auto_ptr.")); ps->comment(string("ps:")); auto_ptr<Report> p1; p1 = ps; //赋值完毕后ps已经失去对内存对象的所有权,不可再使用 p1->comment(string("p1:")); //ps->comment(string("after p1=ps:")); //error,Segmentation faul}/*Result:Object created!Using auto_ptr.Object deleted!*/ auto_ptr的构造函数接受new操作符或者对象工厂创建出的对象指针作为参数,从而代理了原始指针。虽然它是一个对象,但因为重载了 operator*后operator->,其行为非常类似指针,可以把它用在大多数普通指针可用的地方。当退出作用域时(离开作用域或异 常),C++会保证auto_ptr对象销毁,调用auto_ptr的析构函数,进而使用delete操作符删除原始指针释放资源。

3、 auto_ptr很好用,被包含在C++标准库中令它在世界范围内被广泛使用,使用智能指针的思想、用法深入人心。但标注库没有覆盖智能指针的全部领域,尤其最重要的引用计数型智能指针。

shared_ptr智能指针(C++11)

1、所属头文件:#include <memory>所属命名空间及标识符:using std::shared_ptr;所属版本:C++11g++启用版本命令:g++ -std=c++11 -c -o

2、 shared_ptr是一个最像智能指针的“智能指针”,是源自boost库,后被收录到C++11标准的TR1库中。“抱歉,我实在想不出什么更恰当的词汇来形容它在软件开发中的重要性。再强调一遍,shared_ptr非常有价值、非常重要、非常有用。”(摘自《Boost程序库完全开发指南:深入C++“准”标准库》 罗剑锋 著 P69)。 在C++历史上曾经出现过无数的引用计数型智能指针实现,但没有一个比得上shared_ptr,在过去、现在和将来,它都是最好的。

3、shared_ptr模板类摘要:template<class T>class shared_ptr{public: typedef T element_type; // shared_ptr(); template<class Y> explicit shared_ptr(Y *p); template<class Y, class D> shared_ptr(Y *p, D d); ~shared_ptr(); // shared_ptr( shared_ptr const & r); template<calss Y> explicit shared_ptr(std::auto_ptr<Y> & r); // shared_ptr &operator=(shared_ptr const & r); template<class Y> shared_ptr &operator=(shared_ptr<Y> const &r); template<class Y> shared_ptr &operator=(std::auto_ptr<Y> & r); // void reset( ); template<class Y> void reset(Y * p); template<class Y, class D> void reset( Y * p, D d); // T & operator*( )const; T * operator->( ) const; T * get( ) const; // bool unqiue( ) const; long use_count( ) const; // operator unspecified-bool-type( ) const; void swap(shared_ptr & b);}

4、操作函数: shared_ptr是用于管理new动态分配对象的智能指针,它重载了*和->操作符以模仿原始指针的行为,提供隐式bool类型转换以判断指针的有效性,get()函数可以得到指针原始指针,并且没有提供指针算术操作。例如:#include <memory>#include <assert>//using std::shared_ptr;using std::assert;//shared_ptr<int> spi (new int); //一个int的shared_ptrassert(spi); //在bool语境中隐式转换为bool值*spi = 253; //使用解引用操作符*shared_ptr<string> sps(new string("smart")); //一个string的shared_ptrassert(sps->size( ) == 5); //使用->运算符

5、 shared_ptr可以被安全的共享——shared_ptr是一个“全功能”的类,有着正常的拷贝、赋值语义,也可以进行shared_ptr间的比较,是“最智能”的智能指针。 shared_ptr有多种形式的构造函数,应用于各种可能的情形:▲ 无参的shared_ptr( )创建一个持有空指针的shared_ptr;▲ shared_ptr(Y *p)获得指向类型T的指针p的管理权,同时引用计数置为1。 这个构造函数要求Y类型必须能够转换为T类型;▲ shared_ptr(shared_ptr const & r)从另外一个shared_ptr获得指针的管理 权,同时引用计数加1,结果是两个shared_ptr共享一个指针的管理权;▲ shared_ptr(std::auto_ptr<Y> & r)从一个auto_ptr获得指针的管理权,引用 计数置为1,同时auto_ptr自动失去管理权;▲ operator=赋值操作符可以从另外一个shared_ptr或auto_ptr获得指针的 管理权,其行为同构造函数;▲ shared_ptr( Y *p, D d)行为类似shared_ptr(Y * p),但使用参数d指定了 析构时的定制删除器,而不是简单的delete。

6、 shared_ptr的reset( )函数的作用是将引用计数减1,停止对指针的共享,除非引用计数为0,否则不会发生删除操作。 shared_ptr有两个专门的函数检查引用计数。unique( )在shared_ptr是指针的唯一拥所有者时返回true。use_count( )返回当前指针的引用计数。 要小心,use_count( )应该仅仅用于测试或者调试,它不提供高效率的操作,而且有的时候可能是不可用的(极少数情形)。而unique( )则是可靠的,任何时候都可用,而且比use_count( ) == 1 速度更快。

7、 shared_ptr还支持比较运算符,可以测试两个shared_ptr的相等或者不等,比较基于内部保存的指针,相当于a.get( ) == b.get( )。 shared_ptr还可以使用operator<比较大小,同样基于内部保存的指针,但不提供除operator<以外的比较操作符,这使得shared_ptr可以被用于标准关联容器(set 和 map):typedef shared_ptr<string> sp_t; //shared_ptr类型定义map<sp_t, int> m; //标准映射容器sp_t sp(new string("one")); //一个shared_ptr对象 m[sp] = 111; //关联数组用法

8、 shared_ptr还支持流输出操作符operator<<,输出内部的指针值,方便调试。

9、用法举例A.示范shared_ptr基本用法的例子如下:shared_ptr<挢旗扦渌;int> sp (new int(10));assert( sp.unique( )); //现在shared_ptr是指针的唯一持有者shared_ptr<int> sp2; //生成一个空(NULL)的智能指针sp2 = sp; //sp2和sp指向同一个对象,拷贝构造函数//两个shared_ptr相等,指向同一个对象,引用计数为2assert( sp == sp2 && sp.use_count( ) == 2 );*sp2 = 100;assert( *sp == 100 );sp.reset( );assert( !sp );////B.第二个示范shared_ptr复杂的应用:class shared{private: shared_ptr<int> p;public: shared (shared_ptr<int> p_):p(p_) { } void print( ) { cout <<"count:"<<p.use_count() <<"v="<<*P << endl;}};void print_func(shared_ptr<int> p){ //同样输出shared_ptr的引用计数和指向的值 cout << "count:" << p.use_count() << "v =" << *P << endl;}int main( ){ shared_ptr<int > p( new int(100) ); shared s1(p), s2(p); // s1.print( ); s2.print( ); // *p = 20; print_func(p); // s1.print( );}////// 运行结果 //////cout:3 v=100cout:3 v=100cout:4 v=20cout:3 v=20//////////////////////在声明了shared_ptr和两个shared_ptr类实例后,指针被它们所共享,因此引用计数为3。print_func( )函数内部拷贝了一个shared_ptr对象,因此引用计数再增加1,但当退出函数时拷贝自动析构,引用计数又恢复为3。

unique_ptr智能指针(C++11)

1、所属头文件:#include <memory>所属命名空间及标识符:using std::shared_ptr;所属版本:C++11g++启用版本命令:g++ -std=c++11 -c -o

2、unique_ptr为何优于auto_ptr请看下面的语句:auto_ptr<string> p1 (new string("auto")); //#1auto_ptr<string> p2; //#2p2 = p1; //#3 在语句#3中,p2接管string对象的所有权后,p1的所有权将被剥夺。这是件好事,可以防止p1和p2的析构函数试图删除同一个对象;但如果程序随后试图使用p1,这将是件坏事,因为p1不再指向有效的数据。 下面来看看使用unique_ptr的情况:unique_ptr<string> p3 (new string("auto")); //#4unique_ptr<string> p4; //#5p4 = p3; //#6 编译器认为语句#6非法,避免了p3不再指向有效数据的问题。因此,unique_ptr比auto_ptr更安全(编译阶段错误比潜在的程序崩溃更安全)。

3、 但有时候,将一个智能指针赋值给另一个并不会留下危险的悬挂指针(就是空指针,极有可能被误用)。假设有如下函数定义:#include <iostream>#include <m髫潋啜缅emory>#include <string>using std::string;using std::cout;using std::unique_ptr;class Report{private: string str;public: Report( const string s):str(s) { cout << "Object created!\n"; } ~Report() { cout << "Object deleted!\n"; } void comment(const string owner) const { cout << owner << str << "\n"; }};unique_ptr<Report> demo(const char *s){ unique_ptr<Report> temp(new Report(s)); return temp;}int main(void){ unique_ptr<Report> ps; ps = demo("Uniquely special point"); ps->comment(string("un_ptr:")); return 0;}////////////////////////////////////////////////////////// demo( )返回一个临时的unique_prt,然后ps接管了原本归返回的unique_ptr所有的对象,而返回的unique_ptr被销毁。这没有问题,因为ps拥有了Report对象的所有权。这里还有另一个好处是,demo()返回的临时unique_ptr很快被销毁(因为由函数调用而返回的临时对象在堆中使用完后会被销毁),没有机会使用它来访问无效的数据。换句话说,没有理由禁止这种赋值。神奇的是,编译器(GUN GCC g++编译器支持这种特性)确实允许这种赋值!

4、 总之,程序试图将一个unique_ptr赋给另一个时,如果源un坡纠课柩ique_ptr是个临时右值,编译器允许这样做;如磨营稼刻果源unique_ptr将存在一段时间,编译器将禁止这样做:using std::unique_ptr;using std::string;unique_ptr<string> pu1(new string("Hi ho!"));unique_ptr<string> pu2;pu2 = pu1; //#not allowedunique_ptr<string> pu3;pu3 = unique_ptr<string>(new string("Yo!")); //#allowed 语句#1将留下悬挂的unique_ptr(pu1),这句可能导致危害。语句#2不会留下悬挂的unique_ptr,因为它调用unique_ptr的构造函数,该构造函数创建的临时对象在其所有权转让给pu后就被销毁。这种情况而异的行为表明,unique_ptr优于允许两种赋值的auto_ptr。 这也是禁止(只是一种建议,编译器并不禁止)在容器对象中使用auto_ptr,但允许使用unique_ptr的原因。如果容器算法视图对包含unique_ptr的容器执行类似于语句#1的操作,将导致编译错误;如果算法视图执行类似于语句#2的操作,则不会有任何问题。而对auto_ptr,类似于语句#1的操作可能导致不确定的行为和神秘崩溃。 当然,您可能确实想执行类似于语句#1的操作。仅当以非智能的方式使用遗弃的智能指针(如解除引用时),这种赋值才不安全。要安全地重用这种指针,可以给它赋新值。C++有一个标准库函数std::move( ),让您能够将一个unique_ptr赋给另一个。下面是一个使用前述demo( )函数的例子,该函数返回一个unique_ptr<string>对象:using std::unique_ptr;unique_ptr<string> ps1,ps2;ps1 = demo("Uniquely special");ps2 = std::move(ps1); //enable assignment,启用分配ps1 = demo(" and more");cout << *ps2 << *ps1 << endl;

5、 您可能会问,unique_ptr如何能够区分安全和不安全的用法呢?答案是它使用了C++11新增的移动构造函数和右值引用,这将在18章(C++ primer 6th)讨论。 相比于auto_ptr,unique_ptr还有另一个优点,它有一个け用于数组的变体。别忘了,必须将delete和new别对,将delete[]和new[]配对。模板auto_ptr使用delete而不是delete[],因此只能与new一起使用,而不能与new[]一起使用。但unique_ptr有使用new[]和delete[]的版本:std::unique_ptr<double[ ]> pda (new double[5]); //将使用delete[ ]

6、std::default_delete 缇吾怩舭—— 配合unique_ptr使用----------------幻腾寂埒------------------------------------------------------------non-specialized template <class T> class default_delete;array specialization template <class T> class default_delete<T[]>;模板参数:T类型T为待删除对象的类型。------------------------------默认的删除器(Default deleter) 函数对象类,其功能像调用(invokation)并删除一个类型为T*对象。 无参数的版本只是简单的使用delete执行内存释放操作。 同样(likewise), 以运行时确定数组长度的数组参数版本使用delete[]执行内存释放操作 此类用于保存unique_ptr实例中类型删除器而特意设计,或者用于定义一个删除器对象传递给unique_ptr 和 shared_ptr的构造函数。由于这是一个轻量级(lightweight)的无状态类,使用它作为unique_ptr对象的删除器,相对于C++内建指针(绝大多数库都实现了)它既不增加额外开销也不增加空间。-------------------------------- Example ----------------------------------// unique_ptr constructor example#include <iostream>#include <memory>int main () { std::default_delete<int> d; std::unique_ptr<int> u4 (new int, d); std::unique_ptr<int> u5 (new int, std::default_delete<int>()); std::cout << "u4: " << (u4?"not null":"null") << '\n'; std::cout << "u5: " << (u5?"not null":"null") << '\n' return 0;}

new和new[]与智能指针的正确搭配

1、▲使用new分配内存时,才能使用auto_ptr和shared_ptr,使用new[ ]分配内存时,不能使用它们;▲不使用new分配内存时,不能使用auto_ptr或shared_ptr;▲不使用new或new[ ]分配内存时,不能使用unique_ptr;

选择智能指针

1、P67316.2.4

  • linux(Ubuntu)系统如何使用支付宝(余额宝)
  • 300英雄盗贼娃攻略
  • tomcat下多个项目 怎么重启指定的项目
  • 干细胞和神经性疾病有什么关系?
  • 猎杀潜航5怎么回到基地
  • 热门搜索
    手抄报读书伴我成长 安全教育手抄报资料 我运动我健康手抄报 体育节手抄报 规范语言文字手抄报 光盘行动手抄报 一年级教师节手抄报 万圣节手抄报图片 读书手抄报图片大全 放飞梦想手抄报图画