• C++11新特性之智能指针


      1、shared_ptr:一种计数指针,被指向的对象在引用计数为0时删除。它表示共享的所有权(负责对象的删除销毁)。需要包含<memory>,下同。

    // 定义删除器
    struct Deleter
    {
    public:
        void operator() (Base *p)
        {
            cout << "[deleter called]" << endl;
            delete p;
        }
    };
    
    int main()
    {
        shared_ptr<Base> p1(new Base, Deleter());
        {
            shared_ptr<Base> p2(p1);    // 这里p1/p2的use_count()为2
            {
                shared_ptr<Base> p3(p2);    // 这里p1/p2/p3的use_count()为3
            }    // p1/p2的use_count()变为2
        }    // p1的use_count()变为1
    
        p1.reset();    // 无参的reset()将使p1变为空(就像默认构造时一样)
    
        // p1的use_count()变为0且上面的new Base被删除/析构
    
        return 0;
    }

      以下用法是错误的:

    // 错误1:既使用shared_ptr,又自己管理原生指针。原生指针被重复析构
    int *p = new int(9);
    shared_ptr<int> sp1(p);
    delete p;
    
    // 错误2:多个shared_ptr均从同一个原生指针构造。原生指针被重复析构
    // 多个shared_ptr管理同一个原生指针,则后续的shared_ptr都应直接或间接(通过weak_ptr)从第一个构造
    int *q = new int(10);
    shared_ptr<int> sp2(q);
    shared_ptr<int> sp3(q);

      有时候,需要在类的成员函数里面获取到“指向”当前对象的shared_ptr(然后把它传递给其他函数)。此时,只需要继承enable_shared_from_this,即可在成员函数中使用shared_from_this()获取到shared_ptr。示例:

    class Foo;
    
    class Bar
    {
    public:
        void test(shared_ptr<Foo> sp);
    };
    
    class Foo : public enable_shared_from_this<Foo>
    {
    public:
        void test();
        void print();
    };
    
    void Bar::test(shared_ptr<Foo> sp)
    {
        // 此时sp.use_count()为3
        sp -> print();
    }
    
    void Foo::test()
    {
        // 若换成shared_ptr<Foo> sp = shared_ptr<Foo>(this);会出错
        // 这相当于使用一个原生指针分别构造两个shared_ptr,会造成重复释放的问题
        shared_ptr<Foo> sp = shared_from_this();  // 此时sp.use_count()为2
        Bar bar;
        bar.test(sp);
        // 此时sp.use_count()为2
    }
    
    void Foo::print() { cout << "in Foo::print" << endl; }
    
    int main()
    {
        shared_ptr<Foo> sp1(new Foo);
        sp1 -> test();
        // 此时sp1.use_count()为1
    
        // !!!错误用法,抛出bad_weak_ptr异常!!!
        // 原因(参考boost的enable_shared_from_this.hpp):enable_shared_from_this有一个weak_ptr类型的成员,
        // 它只能由shared_ptr通过enable_shared_from_this的一个成员函数赋值(故使用以下方式时,该成员为空),
        // shared_from_this()正是通过该成员“获取”到shared_ptr
        Foo *fp = new Foo;
        fp -> test();
        delete fp; 
    }

      shared_ptr对象可以在共享一个指针P(owned pointer)所有权(负责其释放)的同时保存另一个指针P'(stored pointer,不负责其释放)。通常P和P'是相同的,但通过别名构造器(aliasing constructor)可使shared_ptr拥有不同的P和P':

    struct C
    {
        C() : data(new int(10)) {}
        ~C() {delete data; data = NULL;}
        int *data;
    };
    
    int main ()
    {
        std::shared_ptr<C> obj(new C);
        // sp1和sp2的use_count均为3
        // shared_ptr的*运算符和get()函数操作的均是其保存的指针P',如*sp1==10
    // 另外,注意sp1和sp2的类型是其保存的指针P'的类型! std::shared_ptr<int> sp1(obj, obj -> data); std::shared_ptr<int> sp2(sp1);
    return 0; }

      关于类型转换:

    template <class T, class U>
    shared_ptr<T> static_pointer_cast (const shared_ptr<U>& sp) noexcept;

      static_pointer_cast即shared_ptr的static_cast:(sp非空时)返回的对象与sp共享owned pointer,且将sp的stored pointer转换为T*类型(这里需要确保static_cast<T*>(sp.get())是合法的)。如下例:

    class Test {};
    class Base {};
    class Derived : public Base {};
    
    int main()
    {
        std::shared_ptr<Test> tsp(new Test);
        std::shared_ptr<Base> bsp0(new Base);
        std::shared_ptr<Base> bsp(tsp, bsp0.get());
        std::shared_ptr<Derived> dsp = std::static_pointer_cast<Derived>(bsp);
        // 现在tsp、bsp0、bsp和dsp的use_count分别为3、1、3、3
    
        return 0;
    }

      关于排序:重载的<运算符考虑的是stored pointer的地址;而owner_before()考虑的则是owned pointer的地址,它的排序结果在shared_ptr被用于关联容器(如map)的key时使用,两个shared_ptr对象仅在共享同一指针或均为空时相等。

      2、weak_ptr:Weak shared pointer

    int main()
    {
        shared_ptr<int> sp1(new int(10));
    
        // 可以用shared_ptr/weak_ptr构造weak_ptr
        // weak_ptr拥有资源的访问权,但没有所有权(不会增加引用计数)
        weak_ptr<int> wp(sp1);  // wp和sp1的use_count()均为1
    
        if (!wp.expired())
        {
            // lock():从weak_ptr“获取”一个可用的shared_ptr对象
            shared_ptr<int> sp2 = wp.lock();  // wp和sp1的use_count()均为2
            *sp2 = 100;
        }
    
        // 此时wp和sp1的use_count()均为1
    
        return 0;
    }

      expired():检查weak_ptr对象是否失效(为空或已经没有shared_ptr和它一起观测某个资源)。和空weak_ptr一样,失效的weak_ptr不能用于还原出shared_ptr(lock())。expired()和use_count()==0返回相同的结果,但效率更高。示例:

    weak_ptr<int> wp;    // 此时wp.expired()为1
    
    shared_ptr<int> sp1(new int);
    wp = sp1;
    shared_ptr<int> sp2 = wp.lock();
    
    // 此时wp.expired()为0
    
    sp2.reset();    // 此时wp.expired()为0
    sp1.reset();    // 此时wp.expired()为1

      另外,weak_ptr没有重载*和->运算符,不能像普通指针一样使用(要借助lock(),参考第一个例子中的sp2)。

      shared_ptr还有“环状引用”的问题,也需要使用weak_ptr来规避,如下例:

    class parent;
    class children;
    typedef shared_ptr<parent> parent_ptr;  // 改为typedef weak_ptr<parent> parent_ptr;方可避免“环状引用”的问题
    typedef shared_ptr<children> children_ptr;  // 改为typedef weak_ptr<children> children_ptr;方可避免“环状引用”的问题
    
    class parent
    {
    public:
        children_ptr children;
    };
    
    class children
    {
    public:
        parent_ptr parent;
    };
    
    int main()
    {
        shared_ptr<parent> father(new parent);
        shared_ptr<children> son(new children);
        father -> children = son;
        son -> parent = father;  // father和son的use_count()均为2,程序退出时没有调用parent和children的析构函数!!!
    
        return 0;
    }

      3、unique_ptr:

      1)在了解unique_ptr之前,先来回顾一下auto_ptr。

      auto_ptr:Automatic Pointer(自C++11起已弃用)。

      auto_ptr对象对它管理的指针拥有所有权,即负责其内存释放(释放时机:auto_ptr对象销毁时)。

      和shared_ptr一样,用同一个原生指针初始化多个auto_ptr对象会导致重复释放内存的错误。

    class Base
    {
    public:
        Base() {cout << "in Base" << endl;}
        ~Base() {cout << "in ~Base" << endl;}
        void test() {cout << "in test" << endl;}
    };
    
    int main()
    {
        auto_ptr<int> ap1(new int);
        // 基本操作
        *ap1 = 10;
        int *p = ap1.get();  // *p == 10
        auto_ptr<Base> ap2(new Base);
        ap2 -> test();
    
        auto_ptr<Base> ap3 = ap2; // 赋值或“复制”构造时会转移所有权(故不能用于STL容器中):ap2.get()变成NULL
        ap3 -> test();
    
        auto_ptr<Base> ap4(new Base);
        Base *bp = ap4.release();  // ap4.get()变成NULL。释放所有权,不再对之前管理的指针(的内存)负责。返回之前管理的指针
        delete bp;
    
        auto_ptr<Base> ap5(new Base);
        ap5.reset(); // “显式”销毁/析构其指向的对象。可设置指向新的对象
    
        return 0;
    }

      2)unique_ptr:管理指针(负责其内存释放),提供有限的垃圾回收机制,相比于原生指针几乎没有额外开销(取决于使用的deleter)。

      (1)只支持move赋值/构造,不支持拷贝赋值/构造(因为它们被定义为deleted function):

    int main ()
    {
        std::unique_ptr<int> foo(new int (101));
        std::unique_ptr<int> bar;
    
        // bar = foo;  // 报错:use of deleted function
        bar = std::move(foo);  // 此时foo == nullptr,*bar == 101
    
        return 0;
    }
    // 拷贝构造、拷贝赋值均不被允许
    unique_ptr (const unique_ptr&) = delete;
    unique_ptr& operator= (const unique_ptr&) = delete;

      参考资料:

      http://www.cplusplus.com

    不断学习中。。。

  • 相关阅读:
    Linux编程之自定义消息队列
    MVC5学习系列--Razor视图(一)
    JS将秒转换为 天-时-分-秒
    自己封装了一个EF的上下文类.,分享一下,顺便求大神指点
    VS2015企业版,社区版,专业版详细对比
    [干货来袭]C#6.0新特性
    WebApp上滑加载数据...
    用SignalR 2.0开发客服系统[系列5:使用SignalR的中文简体语言包和其他技术点]
    用SignalR 2.0开发客服系统[系列4:负载均衡的情况下使用SignalR]
    用SignalR 2.0开发客服系统[系列3:实现点对点通讯]
  • 原文地址:https://www.cnblogs.com/hanerfan/p/5022733.html
Copyright © 2020-2023  润新知