shared_ptr一个不需要释放内存的智能指针

一、产生的原因

shared_ptr的产生与unique_ptr类似,都是为了解决raw pointer的new和delete的成对使用,导致的野指针、内存泄漏、重复释放内存等。

不过shared_ptr与unique_ptr场景又有所不同,这里主要是一个raw pointer在不同的代码块之间传来传去的场景,或者指针指向的内存比较大,这段内存可以切分成很多小部分,但是他们却需要共享彼此的数据。参考官方文档:

std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object.

https://en.cppreference.com/w/cpp/memory/shared_ptr

二、特性

shared_ptr 有两个特性:
特性1: 对raw pointer进行了一层封装,让C++程序员不用在担心何时去释放分配好的内存。

特性2: 共享,使用shared_ptr的指针可以共享同一块内存中的数据。

思想是:该类型智能指针在实现上采用的是引用计数机制,即便有一个 shared_ptr 指针放弃了堆内存的“使用权”(引用计数减 1),也不会影响其他指向同一堆内存的 shared_ptr 指针(只有引用计数为 0 时,堆内存才会被自动释放)。

参见官方文档:

The object is destroyed and its memory deallocated when either of the following happens:
1.the last remaining shared_ptr owning the object is destroyed; 

2.the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().

https://en.cppreference.com/w/cpp/memory/shared_ptr

三、常用操作的示例

1. 构造函数:

#include <memory>
#include <iostream>
 
struct Foo {
    Foo() { std::cout << "Foo...\n"; }
    ~Foo() { std::cout << "~Foo...\n"; }
};
 
struct D { 
    void operator()(Foo* p) const {
        std::cout << "Call delete from function object...\n";
        delete p;
    }
};
 
int main()
{
    {
        std::cout << "constructor with no managed object\n";
        std::shared_ptr<Foo> sh1;
        bool ok = sh1.get()==nullptr;
        std::cout<<ok<<'\n';
    }
    // copy构造函数的话,引用计数都会增加
    {
        std::cout << "constructor with object\n";
        std::shared_ptr<Foo> sh2(new Foo);
        std::shared_ptr<Foo> sh3(sh2);
        std::cout << sh2.use_count() << '\n'; 
        std::cout << sh3.use_count() << '\n';
    }
   // 可以指定删除的函数,并传递给构造函数
    {
        std::cout << "constructor with object and deleter\n";
        std::shared_ptr<Foo> sh4(new Foo, D());
        std::shared_ptr<Foo> sh5(new Foo, [](auto p) {
           std::cout << "Call delete from lambda...\n";
           delete p;
        });
    }
}
输出:
constructor with no managed object
1 // shared_ptr 默认构造函数分配的是空指针
constructor with object
Foo...
2 // sh2 和sh3指向的都是同一个内存,所以他们的引用计数都是2
2
~Foo...
constructor with object and deleter
Foo...
Foo...
Call delete from lambda...
~Foo...
Call delete from function object...
~Foo..

2.reset  :

#include <memory>
#include <iostream>
 
struct Foo {
    Foo(int n = 0) noexcept : bar(n) {
        std::cout << "Foo: constructor, bar = " << bar << '\n';
    }
    ~Foo() {
         std::cout << "Foo: destructor, bar = " << bar << '\n';
    }
    int getBar() const noexcept { return bar; }
private:
    int bar;
};
 
int main()
{
    std::shared_ptr<Foo> sptr = std::make_shared<Foo>(1);
    std::cout << "The first Foo's bar is " << sptr->getBar() << "\n";
    std::cout<<"refer_count:"<<sptr.use_count()<<std::endl;
    // reset the shared_ptr, hand it a fresh instance of Foo
    // (the old instance will be destroyed after this call)
    sptr.reset(new Foo);
    std::cout << "The second Foo's bar is " << sptr->getBar() << "\n";
    std::cout<<"refer_count:"<<sptr.use_count()<<std::endl;
}
输出:
Foo: constructor, bar = 1 // 初始化第一个指针
The first Foo's bar is 1
refer_count:1 // 这里会把引用计数设置为1
Foo: constructor, bar = 0 // 初始化第二个指针
Foo: destructor, bar = 1 // 析构掉第1个指针
The second Foo's bar is 0 // 验证第二个指针的变量是0
refer_count:1 // 第一个释放了,第二个新建了,引用计数还是1
Foo: destructor, bar = 0 // 退出main函数析构掉

3. make_shared 

// make_shared example
#include <iostream>
#include <memory>


int main () {
  std::shared_ptr<int> foo = std::make_shared<int> (10);
  // same as: make_shared是推荐的用法,因为它会一次性将raw pointer和引用计数的内存同时分配好
  std::shared_ptr<int> foo2 (new int(10));
  auto bar = std::make_shared<int> (20);
  auto baz = std::make_shared<std::pair<int,int>> (30,40);
  std::cout << "*foo: " << *foo << '\n';
  std::cout << "*bar: " << *bar << '\n';
  std::cout << "*baz: " << baz->first << ' ' << baz->second << '\n';


  return 0;
}
输出:
*foo: 10
*bar: 20
*baz: 30 40

4.allocate_shared

// allocate_shared example
#include <iostream>
#include <memory>
int main () {
  std::allocator<int> alloc;    // the default allocator for int
  std::default_delete<int> del; // the default deleter for int
  std::shared_ptr<int> foo = std::allocate_shared<int> (alloc,10);
  
  auto bar = std::allocate_shared<int> (alloc,20);
  auto baz = std::allocate_shared<std::pair<int,int>> (alloc,30,40);
  
  std::cout << "*foo: " << *foo << '\n';
  std::cout << "*bar: " << *bar << '\n';
  std::cout << "*baz: " << baz->first << ' ' << baz->second << '\n';
  return 0;
}
输出:
*foo: 10
*bar: 20
*baz: 30 40

四、主要函数实现

shared_ptr的代码声明如下所示:

// file: memory


template<class _Tp>>
class shared_ptr {
public:
    typedef _Tp element_type;
private:
    element_type*           __ptr_; // raw pointer的指针
    __shared_weak_count*    __cntrl_; // 引用计数的实现也是指针


    struct __nat{int __for_bool_;}; // placeholder


    // ...
};
// 如果 Yp* 能够转换成 _Tp*,则可以由 _Yp* 构造一个shared_ptr<_Tp>
template<class _Tp>
template<class _Yp>
shared_ptr<_Tp>::shared_ptr(_Yp* __p,
        typename enable_if<is_convertible<_Yp*, element_type*>::value, __nat>::type)
        : __ptr_(__p) {
        unique_ptr<_Yp> __hold(__p);
        typedef __shared_ptr_pointer<_Yp*, default_delete<_Yp>, allocator<_Yp> >_CntrBlk;
        __cntrl_ = new _CntrBlk(__p, default_delete<_Yp>(), allocator<_Yp>());
        __hold.release();
}


// copy constructor, increment reference count
template<class _Tp>
inline shared_ptr<Tp>::shared_ptr(const shared_ptr& __r) noexcept
    : __ptr(__r.__ptr_), __cntrl_(__r.__cntrl_) {
    if (__cntrl_)
        __cntrl_->__add_shared();
}


// move constructor, does't increment reference count
template<class _Tp>
inline shared_ptr<T>::shared_ptr(shared_ptr&& __r) noexcept
    : __ptr_(__r.__ptr_), __cntrl_(__r.__cntrl) {
        __r.__ptr_ = 0;
        __r.__cntrl_ = 0;
}


template<class _Tp>
shared_ptr<_Tp>::~shared_ptr(){
    if (__cntrl_) // 根据引用计数的数量来进行不同的操作
        __cntrl_->__release_shared();
}

涉及到的引用计数类的声明



class __shared_count {  // 引用计数的操作类
    // not copy constructible and not assignable
    __shared_count(const __shared_count&);
    __shared_count& operator=(const __shared_count&);


protected:
    long __shared_owners_; // how many owners do I have?
    virtual ~__shared_count();


public:
    explicit __shared_count(long __refs = 0) noexcept 
: __shared_owners(__refs){}


        void __add_shared() noexcept;
        bool __release_shared() noexcept;
};


class __shared_weak_count : private __shared_count {
    long __shared_weak_owners_;


public:
    explicit __shared_weak_count(long __refs = 0) noexcept {
        : __shared_count(__refs), 
          __shared_weak_owners(__refs) {}


protected:
    virtual ~__shared_weak_count();


public:
    void __add_shared() noexcept;
    void __add_weak() noexcept;
    void __release_shared() noexcept;
    void __release_weak() noexcept;
    long use_count() const noexcept { return __shared_count::use_count();}


private:
    virtual void __on_zero_shared_weak() noexcept = 0;
};

补充资料:
http://www.cplusplus.com/reference/memory/shared_ptr/shared_ptr/

https://en.cppreference.com/w/cpp/memory/shared_ptr

http://c.biancheng.net/view/7898.html

https://blog.csdn.net/thinkerleo1997/article/details/78754919

https://blog.csdn.net/shaosunrise/article/details/85228823

https://www.jianshu.com/p/d365bfbb83a3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值