zoukankan      html  css  js  c++  java
  • shared_ptr 用法

    引入

    shared_ptr 是c++为了提高安全性而添加的智能指针,方便了内存管理。

    特点

    shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。这便是所谓的引用计数(reference counting)。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。使得指针可以共享对象,并且不用考虑内存泄漏问题

    shared_ptr 可以支持普通指针的所有操作,完全可以像操作普通指针一样操作智能指针。
    shared_ptr 可以通过三种方式得到(拷贝初始化,定义delete操作的方式不在罗列,只讨论初始化指针所指对象来源):
    1.通过一个指向堆上申请的空间的指针初始化(切记不要用栈上的指针,否则,当智能指针全部释放控制权(栈中的对象离开作用域本身就会析构一次),将会析构对象,导致出错)
    2.通过make_shared函数得到
    3.通过另外一个智能指针初始化

        int *a = new int(8);
        std::shared_ptr<int> p1(a);
    
        auto b = std::make_shared<int>(2);
    
        auto c(b);

    注意事项

    1. 使用原生指针多次初始化

    class Base
    {
    public:
        Base() {
            printf("con
    ");
        }
        ~Base() {
            printf("decon
    ");
        }
    };
    
    
    int main()
    {
        Base *a = new Base();
    
        std::shared_ptr<Base> p1(a);
        std::shared_ptr<Base> p2(a);
        
        return 0;
    }

    这段代码,导致调用一次构造函数,两次析构函数

    2. 使用一个 shared_ptr 的 get() 初始化另一个 shared_ptr

        Base *a = new Base();
    
        std::shared_ptr<Base> p1(a);
        std::shared_ptr<Base> p2(p1.get());

    cppreference 指明这是未定义行为

    3. 使用 shared_ptr 包装 this 指针

    class Base
    {
    public:
        Base() {
            printf("con
    ");
        }
        ~Base() {
            printf("decon
    ");
        }
    
        std::shared_ptr<Base> sget() {
            return std::shared_ptr<Base>(this);
        }
    };
    
    
    int main()
    {
        Base b;
        std::shared_ptr<Base> p = b.sget();
        
        return 0;
    }

    这会调用两次析构函数。一次是b对象析构时,一次是智能指针销毁时

    正确的使用方法是:

    class Base : public std::enable_shared_from_this<Base>
    {
    public:
        Base() {
            printf("con
    ");
        }
        ~Base() {
            printf("decon
    ");
        }
    
        std::shared_ptr<Base> sget() {
            return shared_from_this();
        }
    };
    
    
    int main()
    {
        std::shared_ptr<Base> b = std::make_shared<Base>();
        std::shared_ptr<Base> a = b->sget();
        
        return 0;
    }

    enable_shared_from_this 能让其一个对象(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ... ) ,它们与 pt 共享对象 t 的所有权。

    4. shared_ptr 包装数组

    shared_ptr 不能直接包装数组,需要指定数组的析构函数。不过 shared_ptr 不支持取下标操,unique_ptr 是支持的

    class Base
    {
    public:
        Base() {
            printf("con
    ");
        }
        ~Base() {
            printf("decon
    ");
        }
    };
    
    
    int main()
    {
        std::shared_ptr<Base> a(new Base[2], [] (Base* p) {
            delete[] p;
        });
        
        return 0;
    }

     5. 循环引用

    class A;
    class B;
    
    using sa = std::shared_ptr<A>;
    using sb = std::shared_ptr<B>;
    
    class A
    {
    public:
        A() {
            printf("A con
    ");
        }
        ~A() {
            printf("A decon
    ");
        }
    
        sb b_;
    };
    
    class B
    {
    public:
        B() {
            printf("B con
    ");
        }
        ~B() {
            printf("B decon
    ");
        }
        
        sa a_;
    };
    
    int main(int argc, char const *argv[])
    {
        sa a(new A);
        sb b(new B);
        
        a->b_ = b;
        b->a_ = a;
        
        return 0;
    }

    对象 a 和 b 都未被析构

    正确的方法是使用 weak_ptr 代替 shared_ptr

    class A;
    class B;
    
    using sa = std::shared_ptr<A>;
    using sb = std::shared_ptr<B>;
    
    class A
    {
    public:
        A() {
            printf("A con
    ");
        }
        ~A() {
            printf("A decon
    ");
        }
    
        std::weak_ptr<B> b_;
    };
    
    class B
    {
    public:
        B() {
            printf("B con
    ");
        }
        ~B() {
            printf("B decon
    ");
        }
        
        std::weak_ptr<A> a_;
    };
    
    int main(int argc, char const *argv[])
    {
        sa a(new A);
        sb b(new B);
        
        a->b_ = b;
        b->a_ = a;
        
        return 0;
    }

     6. 多线程中使用 shared_ptr

    shared_ptr的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。shared_ptr 的线程安全级别和内建类型、标准库容器、std::string 一样,即:

    • 一个 shared_ptr 对象实体可被多个线程同时读取
    • 两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作
    • 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁
  • 相关阅读:
    Reverse Integer
    Reverse Bits
    Number of 1 Bits
    House Robber
    02-线性结构1. 一元多项式求导 (25)
    01-复杂度2. Maximum Subsequence Sum (25)
    <转载>使CSS文字图片div元素居中方法之水平居中的几个方法
    <转载>div+css布局教程之div+css常见布局结构定义
    15个必须知道的chrome开发者技巧
    <转载>Div+Css布局教程(-)CSS必备知识
  • 原文地址:https://www.cnblogs.com/zuofaqi/p/11222144.html
Copyright © 2011-2022 走看看