C++智能指针使用总结

来源:互联网 发布:学神it linux视频下载 编辑:程序博客网 时间:2024/06/16 09:43

C++智能指针使用总结


C++提供了4种智能指针用于对分配的内存进行自动释放,这些智能指针如下:

      auto_ptr、unique_ptr、shared_ptr、weak_ptr。其中auto_ptr在C++98标准引入,后三种在C++11标准中加入。而auto_ptr已经被C++11所摒弃,建议使用后三种智能指针,这4种智能指针使用模板(template)实现。在此总结下个人对这4种智能指针肤浅认识。

C++智能指针简介

  1. 1

           C++智能指针是行为类似于指针的类对象。它使用设计模式中的代理模式,代理了原始“裸”指针的行为,为指针添加了更多更有用的特性。

           C++引入异常机制后,智能指针由一种技巧升级为一种非常重要的技术,因为如果没有智能指针,程序员必须保证new对象能在正确的时机delete,四处编写异常捕获代码释放资源,而智能指针则可以在退出作用域时——不管是正常离开或是因异常离开——总调用delete来析构在堆栈上动态分配的对象。

           因为C++异常处理的真正功能在于它具有为异常抛掷前构造的所有局部对象(那么智能指针对象也适用)自动调用析构函数的能力(C++异常机制不仅仅在于它能够处理各种不同类型的异常)。所以在异常退出智能指针对象作用域时,总能由C++异常机制调用析构函数释放在堆栈上动态分配的对象。

           当然,正常退出对象(智能指针对象也属于此列)作用域也会自动调用析构函数释放在堆栈上动态分配的对象。

           由此可知,将“裸”指针包装成智能指针对象可以实现动态分配的内存对象的自动释放。

           而且C++智能指针对象可以像原指针那样直接使用运算符,如赋值运算符'=',指针运算符'->',解引用运算符'*'。这点可以从下面的”shared_ptr智能指针--shared_ptr模板类摘要“部分可以印证。

    END

auto_ptr智能指针(C++98)

  1. 1

    所属头文件:#include <memory>

    所属命名空间及标识符:using std::shared_ptr;

    所属版本:C++98

    g++启用版本命令:g++ -std=c++98 -c -o

    补充:

           如果启用c++11及以上标准,即g++ -std=c++11 -c -o,编译时会有一个警告信息提示

    warning:‘auto_ptr’ is deprecated  (‘auto_ptr‘被反对使用)

  2. 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. 3

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

    END

shared_ptr智能指针(C++11)

  1. 1

    所属头文件:#include <memory>

    所属命名空间及标识符:using std::shared_ptr;

    所属版本:C++11

    g++启用版本命令:g++ -std=c++11 -c -o

  2. 2

           shared_ptr是一个最像智能指针的“智能指针”,是源自boost库,后被收录到C++11标准的TR1库中。“抱歉,我实在想不出什么更恰当的词汇来形容它在软件开发中的重要性。再强调一遍,shared_ptr非常有价值、非常重要、非常有用。”(摘自《Boost程序库完全开发指南:深入C++“准”标准库》   罗剑锋 著 P69)。

           在C++历史上曾经出现过无数的引用计数型智能指针实现,但没有一个比得上shared_ptr,在过去、现在和将来,它都是最好的。

  3. 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. 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_ptr

    assert(spi);   //在bool语境中隐式转换为bool值

    *spi = 253;   //使用解引用操作符*

    shared_ptr<string> sps(new string("smart"));   //一个string的shared_ptr

    assert(sps->size( ) == 5);   //使用->运算符

  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. 6

           shared_ptr的reset( )函数的作用是将引用计数减1,停止对指针的共享,除非引用计数为0,否则不会发生删除操作。

           shared_ptr有两个专门的函数检查引用计数。unique( )在shared_ptr是指针的唯一拥所有者时返回true。use_count( )返回当前指针的引用计数。

           要小心,use_count( )应该仅仅用于测试或者调试,它不提供高效率的操作,而且有的时候可能是不可用的(极少数情形)。而unique( )则是可靠的,任何时候都可用,而且比use_count( ) == 1 速度更快。

  7. 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. 8

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

  9. 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相等,指向同一个对象,引用计数为2

    assert( 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=100

    cout:3 v=100

    cout:4 v=20

    cout:3 v=20

    //////////////////////

    在声明了shared_ptr和两个shared_ptr类实例后,指针被它们所共享,因此引用计数为3。print_func( )函数内部拷贝了一个shared_ptr对象,因此引用计数再增加1,但当退出函数时拷贝自动析构,引用计数又恢复为3。

    END

unique_ptr智能指针(C++11)

  1. 1

    所属头文件:#include <memory>

    所属命名空间及标识符:using std::shared_ptr;

    所属版本:C++11

    g++启用版本命令:g++ -std=c++11 -c -o

  2. 2

    unique_ptr为何优于auto_ptr

    请看下面的语句:

    auto_ptr<string> p1 (new string("auto"));  //#1

    auto_ptr<string> p2;                                  //#2

    p2 = p1;                                                      //#3

           在语句#3中,p2接管string对象的所有权后,p1的所有权将被剥夺。这是件好事,可以防止p1和p2的析构函数试图删除同一个对象;但如果程序随后试图使用p1,这将是件坏事,因为p1不再指向有效的数据。

           下面来看看使用unique_ptr的情况:

    unique_ptr<string> p3 (new string("auto"));  //#4

    unique_ptr<string> p4;                                   //#5

    p4 = p3;                                                           //#6

           编译器认为语句#6非法,避免了p3不再指向有效数据的问题。因此,unique_ptr比auto_ptr更安全(编译阶段错误比潜在的程序崩溃更安全)。

  3. 3

           但有时候,将一个智能指针赋值给另一个并不会留下危险的悬挂指针(就是空指针,极有可能被误用)。假设有如下函数定义:

    #include <iostream>

    #include <memory>

    #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. 4

           总之,程序试图将一个unique_ptr赋给另一个时,如果源unique_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 allowed

    unique_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. 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. 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;

    }

    END

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

  1. 1

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

    ▲不使用new分配内存时,不能使用auto_ptr或shared_ptr;

    ▲不使用new或new[ ]分配内存时,不能使用unique_ptr;

    END

选择智能指针


从零开始学编程 http://www.c0ks.com
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 贝德玛瓶盖摔坏怎么办 塑料盖子错位拧不开怎么办 安全瓶盖坏了怎么办 刚买面霜打不开怎么办 可乐瓶盖鼓起来怎么办 暖壶塞子吸住了怎么办 茶兀瓶盖打不开怎么办 水杯盖太紧了拧不开怎么办 矿泉水瓶盖拧不开了怎么办 弩弦用手拉不上怎么办 茅台酒瓶口漏酒怎么办 化妆品盖子丢了怎么办 化妆品盖子碎了怎么办 自制水泵压力小怎么办 大学数学不会做怎么办 下雪了怎么办教案幼儿园小班 下水道被混凝土堵塞怎么办 日本足贴丢了胶布怎么办 牙齿被可乐腐蚀怎么办 三十岁满嘴无牙怎么办 水乳盖子打不开怎么办 蜂蜜罐子打不开了怎么办 蜂蜜盖子第二次拧不开怎么办 玻璃杯子拧不开盖子怎么办 玻璃杯水杯盖子拧不开怎么办 鞋子蝴蝶结掉了怎么办 蝴蝶翅膀受伤了怎么办 手被割了个口子怎么办 致炫方向盘重怎么办 黑檀7打不透怎么办 乒乓球底板太轻怎么办 狙击精英4卡怎么办 鼠标点一下变两下怎么办 工程干完不给钱怎么办 屋里有大蛾子怎么办 房间很多小飞虫怎么办 雷蛇键盘失灵怎么办 xp驱动 不支持win10怎么办 阿提拉全面战争统治度太低怎么办 微信号变成wxid怎么办 ipv4 ipv6未连接怎么办