• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

C++ 11 -智能指针

武飞扬头像
cat_fish_rain
帮助1

目录

1.引入:为什么需要智能指针

2. 内存泄漏

2.1 什么是内存泄漏,内存泄漏的危害

2.2 内存泄漏分类(了解)

2.3 如何检测内存泄漏(了解)

2.4如何避免内存泄漏

3.智能指针的使用及原理

3.1 RAII

3.2 智能指针的原理

3.3 std::auto_ptr

3.4 std::unique_ptr

3.5 std::shared_ptr

4.C 11和boost中智能指针的关系

本节主要介绍智能指针的相关用法。

1.引入:为什么需要智能指针

    

下面我们先分析一下下面这段程序有没有什么 内存方面 的问题?提示一下:注意分析 MergeSort函数中的问题。
  1.  
    #include <iostream>
  2.  
    #include <vector>
  3.  
    #include <algorithm>
  4.  
    using namespace std;
  5.  
     
  6.  
    int div()
  7.  
    {
  8.  
     
  9.  
    int a, b;
  10.  
    cin >> a >> b;
  11.  
    if (b == 0)
  12.  
    throw invalid_argument("zero was div");
  13.  
    return a / b;
  14.  
    }
  15.  
     
  16.  
    void Func()
  17.  
    {
  18.  
     
  19.  
    // 1、如果p1这里new 抛异常会如何?
  20.  
    // 2、如果p2这里new 抛异常会如何?
  21.  
    // 3、如果div调用这里又会抛异常会如何?
  22.  
    int *p1 = new int;
  23.  
    int *p2 = new int;
  24.  
     
  25.  
    cout << div() << endl;
  26.  
    delete p1;
  27.  
    delete p2;
  28.  
    }
  29.  
     
  30.  
    int main()
  31.  
    {
  32.  
     
  33.  
    try
  34.  
    {
  35.  
    Func();
  36.  
    }
  37.  
    catch (const std::exception &e)
  38.  
    {
  39.  
    std::cerr << e.what() << '\n';
  40.  
    }
  41.  
     
  42.  
    system("pause");
  43.  
    return 0;
  44.  
    }
学新通

2. 内存泄漏

2.1 什么是内存泄漏,内存泄漏的危害

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现 内存泄漏会导致响应越来越慢,最终卡死。
  1.  
    #include <iostream>
  2.  
    #include <vector>
  3.  
    #include <algorithm>
  4.  
    using namespace std;
  5.  
     
  6.  
    int div()
  7.  
    {
  8.  
     
  9.  
    int a, b;
  10.  
    cin >> a >> b;
  11.  
    if (b == 0)
  12.  
    throw invalid_argument("zero was div");
  13.  
    return a / b;
  14.  
    }
  15.  
     
  16.  
    void Func()
  17.  
    {
  18.  
     
  19.  
    // 1、如果p1这里new 抛异常会如何?
  20.  
    // 2、如果p2这里new 抛异常会如何?
  21.  
    // 3、如果div调用这里又会抛异常会如何?
  22.  
    int *p1 = new int;
  23.  
    int *p2 = new int;
  24.  
     
  25.  
    cout << div() << endl;
  26.  
    delete p1;
  27.  
    delete p2;
  28.  
    }
  29.  
     
  30.  
     
  31.  
    void MemoryLeaks(){
  32.  
     
  33.  
    int *p1=(int*)malloc(sizeof(int));
  34.  
    int *p2=new int;
  35.  
     
  36.  
    int *p3=new int[10];
  37.  
     
  38.  
    //这里Func() 函数抛异常,导致delete []p3 没有执行
  39.  
    Func();
  40.  
     
  41.  
    delete []p3;
  42.  
     
  43.  
     
  44.  
    }
  45.  
     
  46.  
     
  47.  
    int main()
  48.  
    {
  49.  
     
  50.  
    // try
  51.  
    // {
  52.  
    // Func();
  53.  
    // }
  54.  
    // catch (const std::exception &e)
  55.  
    // {
  56.  
    // std::cerr << e.what() << '\n';
  57.  
    // }
  58.  
     
  59.  
    try
  60.  
    {
  61.  
    MemoryLeaks();
  62.  
    }
  63.  
    catch(const std::exception& e)
  64.  
    {
  65.  
    std::cerr << e.what() << '\n';
  66.  
    }
  67.  
     
  68.  
     
  69.  
    system("pause");
  70.  
    return 0;
  71.  
    }
学新通

2.2 内存泄漏分类(了解)

C/C 程序中一般我们关心两种方面的内存泄漏:
  • 堆内存泄漏(Heap leak)
 堆内存指的是程序执行中依据须要分配通过 malloc / calloc / realloc / new 等从堆中分配的一块内存,用完后必须通过调用相应的 free 或者 delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak
  • 系统资源泄漏
  指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

2.3 如何检测内存泄漏(了解)

linux 下内存泄漏检测: linux 下几款内存泄漏检测工具
windows 下使用第三方工具: VLD 工具说明
其他工具: 内存泄漏工具比较

2.4如何避免内存泄漏

1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。 ps :这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智能指针来管理才有保证。
2. 采用 RAII 思想或者智能指针来管理资源。
3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
4. 出问题了使用内存泄漏工具检测。 ps :不过很多工具都不够靠谱,或者收费昂贵。
总结一下 :
内存泄漏非常常见,解决方案分为两种: 1 、事前预防型。如智能指针等。 2 、事后查错型。如泄
漏检测工具

3.智能指针的使用及原理

3.1 RAII

RAII Resource Acquisition Is Initialization )是一种 利用对象生命周期来控制程序资源 (如内
存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源 ,接着控制对资源的访问使之在对象的生命周期内始终保持有效, 最后在
对象析构的时候释放资源 。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:
不需要显式地释放资源。
采用这种方式,对象所需的资源在其生命期内始终保持有效。
  1.  
    #include<iostream>
  2.  
    #include<vector>
  3.  
    #include<algorithm>
  4.  
    using namespace std;
  5.  
     
  6.  
    template<class T>
  7.  
    // 使用RAII思想设计的SmartPtr类
  8.  
     
  9.  
    class smartPtr{
  10.  
     
  11.  
    public:
  12.  
    smartPtr(T *ptr):_ptr(ptr){
  13.  
    cout<<"construct"<<endl;
  14.  
    }
  15.  
     
  16.  
     
  17.  
    ~smartPtr(){
  18.  
    if(_ptr)
  19.  
    {
  20.  
    delete _ptr;
  21.  
    }
  22.  
    cout<<"Destruction"<<endl;
  23.  
    }
  24.  
     
  25.  
     
  26.  
    private:
  27.  
    T *_ptr;
  28.  
     
  29.  
     
  30.  
    };
  31.  
     
  32.  
     
  33.  
    int div(){
  34.  
    int a,b;
  35.  
    cin>>a>>b;
  36.  
     
  37.  
    if(b==0)
  38.  
    throw invalid_argument("zero was div");
  39.  
     
  40.  
    return 0;
  41.  
    }
  42.  
     
  43.  
    void Func(){
  44.  
     
  45.  
     
  46.  
    smartPtr<int> sp1(new int);
  47.  
    smartPtr<int> sp2(new int);
  48.  
     
  49.  
    cout<<div()<<endl;
  50.  
     
  51.  
    }
  52.  
     
  53.  
     
  54.  
     
  55.  
     
  56.  
    int main()
  57.  
    {
  58.  
     
  59.  
    try
  60.  
    {
  61.  
    Func();
  62.  
    }
  63.  
    catch(const std::exception& e)
  64.  
    {
  65.  
    std::cerr << e.what() << '\n';
  66.  
    }
  67.  
     
  68.  
     
  69.  
     
  70.  
     
  71.  
     
  72.  
    system("pause");
  73.  
    return 0;
  74.  
    }
学新通

以下是结果,由此可见,即使抛了异常,类被销毁了也,内存也会释放。

学新通

3.2 智能指针的原理

上述的 SmartPtr 还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可
以通过 -> 去访问所指空间中的内容,因此: AutoPtr 模板类中还得需要将 * -> 重载下,才可让其
像指针一样去使用
  1.  
    #include <iostream>
  2.  
    #include <vector>
  3.  
    #include <algorithm>
  4.  
    using namespace std;
  5.  
     
  6.  
    template <class T>
  7.  
    // 使用RAII思想设计的SmartPtr类
  8.  
     
  9.  
    class smartPtr
  10.  
    {
  11.  
     
  12.  
    public:
  13.  
    smartPtr(T *ptr) : _ptr(ptr)
  14.  
    {
  15.  
    cout << "construct" << endl;
  16.  
    }
  17.  
    //解引用的重载
  18.  
    T &operator*()
  19.  
    {
  20.  
    return *_ptr;
  21.  
    }
  22.  
     
  23.  
    T *operator->()
  24.  
    {
  25.  
    return _ptr;
  26.  
    }
  27.  
     
  28.  
    ~smartPtr()
  29.  
    {
  30.  
    if (_ptr)
  31.  
    {
  32.  
    delete _ptr;
  33.  
    }
  34.  
    cout << "Destruction" << endl;
  35.  
    }
  36.  
     
  37.  
    private:
  38.  
    T *_ptr;
  39.  
    };
  40.  
     
  41.  
    int div()
  42.  
    {
  43.  
    int a, b;
  44.  
    cin >> a >> b;
  45.  
     
  46.  
    if (b == 0)
  47.  
    throw invalid_argument("zero was div");
  48.  
     
  49.  
    return 0;
  50.  
    }
  51.  
     
  52.  
    void Func()
  53.  
    {
  54.  
     
  55.  
    smartPtr<int> sp1(new int);
  56.  
    smartPtr<int> sp2(new int);
  57.  
     
  58.  
    cout << div() << endl;
  59.  
    }
  60.  
     
  61.  
    class Date{
  62.  
    public:
  63.  
    int _year;
  64.  
    int _month;
  65.  
    int _day;
  66.  
     
  67.  
    };
  68.  
     
  69.  
    void test01(){
  70.  
    smartPtr<int> sp1(new int);
  71.  
    *sp1=10;
  72.  
    cout<<*sp1<<endl;
  73.  
     
  74.  
    smartPtr<Date> sp(new Date);
  75.  
    sp->_year=2018;
  76.  
    sp->_month=1;
  77.  
    sp->_day=1;
  78.  
     
  79.  
    cout<<sp->_month<<sp->_year<<sp->_day<<endl;
  80.  
     
  81.  
     
  82.  
    }
  83.  
     
  84.  
     
  85.  
     
  86.  
     
  87.  
     
  88.  
     
  89.  
     
  90.  
    int main()
  91.  
    {
  92.  
     
  93.  
    test01();
  94.  
    // try
  95.  
    // {
  96.  
    // Func();
  97.  
    // }
  98.  
    // catch (const std::exception &e)
  99.  
    // {
  100.  
    // std::cerr << e.what() << '\n';
  101.  
    // }
  102.  
     
  103.  
    system("pause");
  104.  
    return 0;
  105.  
    }
学新通
总结一下智能指针的原理:
1. RAII 特性
2. 重载 operator* opertaor-> ,具有像指针一样的行为。

3.3 std::auto_ptr

C 98 版本的库中就提供了 auto_ptr 的智能指针。下面演示的 auto_ptr 的使用及问题。 auto_ptr 的实现原理:管理权转移的思想,下面简化模拟实现了一份 bit::auto_ptr 来了解它的原
  1.  
    #include<iostream>
  2.  
    #include<vector>
  3.  
     
  4.  
    #include<algorithm>
  5.  
    using namespace std;
  6.  
     
  7.  
     
  8.  
    namespace myspace
  9.  
    {
  10.  
     
  11.  
    template<class T>
  12.  
    class auto_ptr{
  13.  
     
  14.  
    public:
  15.  
    auto_ptr(T *ptr):_ptr(ptr) {
  16.  
     
  17.  
    }
  18.  
     
  19.  
     
  20.  
    auto_ptr(auto_ptr<T> & sp):_ptr(sp._ptr){
  21.  
     
  22.  
    //管理权转移
  23.  
    sp._ptr=nullptr;
  24.  
    }
  25.  
     
  26.  
    auto_ptr<T> operator=(auto_ptr<T> &ap){
  27.  
    if(this!=&ap){
  28.  
    if(_ptr){
  29.  
    delete _ptr;
  30.  
    }
  31.  
     
  32.  
    _ptr=ap._ptr;
  33.  
    ap._ptr=NULL;
  34.  
    }
  35.  
     
  36.  
    return *this;
  37.  
    }
  38.  
     
  39.  
    ~auto_ptr(){
  40.  
    if(_ptr){
  41.  
    cout<<"delete:"<<_ptr<<endl;
  42.  
    delete _ptr;
  43.  
    }
  44.  
    }
  45.  
     
  46.  
    T &operator*(){
  47.  
    return *_ptr;
  48.  
    }
  49.  
     
  50.  
    T *operator->(){
  51.  
    return _ptr;
  52.  
    }
  53.  
     
  54.  
     
  55.  
    private:
  56.  
    T *_ptr;
  57.  
     
  58.  
    };
  59.  
     
  60.  
     
  61.  
    } // namespace myspace
  62.  
     
  63.  
     
  64.  
     
  65.  
     
  66.  
    // 结论:auto_ptr是一个失败设计,很多公司明确要求不能使用auto_ptr
  67.  
     
  68.  
    int main()
  69.  
    {
  70.  
     
  71.  
    myspace::auto_ptr<int> sp1(new int);
  72.  
    myspace::auto_ptr<int> sp2(sp1);
  73.  
     
  74.  
    *sp2=10;
  75.  
    cout<<*sp2<<endl;
  76.  
    cout<<*sp1<<endl;
  77.  
     
  78.  
     
  79.  
     
  80.  
    system("pause");
  81.  
    return 0;
  82.  
    }
学新通

3.4 std::unique_ptr

        

C 11 中开始提供更靠谱的 unique_ptr
unique_ptr 的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份 UniquePtr 来了解它的原
理。
        
// C 11 库才更新智能指针实现
// C 11 出来之前, boost 搞除了更好用的 scoped_ptr/shared_ptr/weak_ptr
// C 11 boost 库中智能指针精华部分吸收了过来
// C 11->unique_ptr/shared_ptr/weak_ptr
// unique_ptr/scoped_ptr
// 原理:简单粗暴 -- 防拷贝
 
  1.  
    #include <iostream>
  2.  
    #include <vector>
  3.  
    #include <memory>
  4.  
    #include <algorithm>
  5.  
    using namespace std;
  6.  
     
  7.  
    namespace myspace
  8.  
    {
  9.  
    template <class T>
  10.  
    class unique_ptr
  11.  
    {
  12.  
    public:
  13.  
    unique_ptr(T *ptr) : _ptr(ptr)
  14.  
    {
  15.  
    }
  16.  
     
  17.  
    ~unique_ptr()
  18.  
    {
  19.  
     
  20.  
    if (_ptr)
  21.  
    {
  22.  
    cout << "delete _ptr" << _ptr << endl;
  23.  
    delete _ptr;
  24.  
    }
  25.  
    }
  26.  
     
  27.  
    T &operator*()
  28.  
    {
  29.  
    return *_ptr;
  30.  
    }
  31.  
     
  32.  
    T *operator->()
  33.  
    {
  34.  
    return _ptr;
  35.  
    }
  36.  
     
  37.  
    //将类中的默认的拷贝构造函数和赋值的重载方法进行了删除
  38.  
    unique_ptr(const unique_ptr<T> &sp) = delete;
  39.  
    unique_ptr<T> &operator=(const unique_ptr<T> &sp) = delete;
  40.  
     
  41.  
    private:
  42.  
    T *_ptr;
  43.  
    };
  44.  
     
  45.  
    } // namespace myspace
  46.  
     
  47.  
    void test01()
  48.  
    {
  49.  
     
  50.  
    // myspace::unique_ptr<int> sp(new int);
  51.  
    // myspace::unique_ptr<int> sp2(sp); 报错
  52.  
     
  53.  
    unique_ptr<int> sp(new int);
  54.  
    // unique_ptr<int> sp2(sp);
  55.  
    }
  56.  
     
  57.  
    int main()
  58.  
    {
  59.  
     
  60.  
    test01();
  61.  
     
  62.  
    system("pause");
  63.  
    return 0;
  64.  
    }
学新通

3.5 std::shared_ptr

C 11 中开始提供更靠谱的并且支持拷贝的 shared_ptr
shared_ptr 的原理:是通过引用计数的方式来实现多个 shared_ptr 对象之间共享资源 。例如: 刘老师晚上在下班之前都会通知,让最后走的学生记得把柄锁下。
1. shared_ptr 在其内部, 给每个资源都维护了着一份计数,用来记录该份资源被几个对象共
2. 对象被销毁时 ( 也就是析构函数调用 ) ,就说明自己不使用该资源了,对象的引用计数减一。
3. 如果引用计数是 0 ,就说明自己是最后一个使用该资源的对象, 必须释放该资源
4. 如果不是 0 ,就说明除了自己还有其他对象在使用该份资源, 不能释放该资源 ,否则其他对象就成野指针了。
// 引用计数支持多个拷贝管理同一个资源,最后一个析构对象释放资源
  1.  
    #include <iostream>
  2.  
    #include <vector>
  3.  
    #include <mutex>
  4.  
    #include <algorithm>
  5.  
    using namespace std;
  6.  
     
  7.  
    namespace myspace
  8.  
    {
  9.  
     
  10.  
    template <class T>
  11.  
    class shared_ptr
  12.  
    {
  13.  
     
  14.  
    public:
  15.  
    shared_ptr(T *ptr = nullptr) : _ptr(ptr), _pRefCount(new int(1)), _pmtx(new mutex)
  16.  
     
  17.  
    {
  18.  
    }
  19.  
     
  20.  
    shared_ptr(const shared_ptr<T> &sp) : _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx)
  21.  
    {
  22.  
    Addref();
  23.  
    }
  24.  
     
  25.  
    void Addref()
  26.  
    {
  27.  
    _pmtx->lock();
  28.  
    (*_pRefCount);
  29.  
    _pmtx->unlock();
  30.  
    }
  31.  
     
  32.  
    void Release()
  33.  
    {
  34.  
     
  35.  
    _pmtx->lock();
  36.  
    bool flag = false;
  37.  
     
  38.  
    if (--(*_pRefCount) == 0 && _ptr)
  39.  
    {
  40.  
     
  41.  
    cout << "delete" << _ptr << endl;
  42.  
    delete _ptr;
  43.  
    delete _pRefCount;
  44.  
    flag = true;
  45.  
    }
  46.  
     
  47.  
    _pmtx->unlock();
  48.  
    if (flag == true)
  49.  
    {
  50.  
    delete _pmtx;
  51.  
    }
  52.  
    }
  53.  
     
  54.  
    shared_ptr<T> &operator=(const shared_ptr<T> &sp)
  55.  
    {
  56.  
     
  57.  
    if (_ptr != sp._ptr)
  58.  
    {
  59.  
    Release();
  60.  
    _ptr = sp._ptr;
  61.  
    _pRefCount = sp._pRefCount;
  62.  
    _pmtx = sp._pmtx;
  63.  
    Addref();
  64.  
    }
  65.  
    return *this;
  66.  
    }
  67.  
     
  68.  
    int use_count()
  69.  
    {
  70.  
    return *_pRefCount;
  71.  
    }
  72.  
     
  73.  
    ~shared_ptr()
  74.  
    {
  75.  
    Release();
  76.  
    }
  77.  
     
  78.  
    T &operator*()
  79.  
    {
  80.  
    return *_ptr;
  81.  
    }
  82.  
     
  83.  
    T *operator->()
  84.  
    {
  85.  
    return _ptr;
  86.  
    }
  87.  
     
  88.  
    T *get() const
  89.  
    {
  90.  
    return _ptr;
  91.  
    }
  92.  
     
  93.  
    private:
  94.  
    T *_ptr;
  95.  
    int *_pRefCount;
  96.  
    mutex *_pmtx;
  97.  
    };
  98.  
     
  99.  
    template <class T>
  100.  
    class weak_ptr
  101.  
    {
  102.  
     
  103.  
    public:
  104.  
    weak_ptr() : _ptr(nullptr)
  105.  
    {
  106.  
    }
  107.  
     
  108.  
    weak_ptr(const shared_ptr<T> &sp) : _ptr(sp.get())
  109.  
    {
  110.  
    }
  111.  
     
  112.  
    weak_ptr<T> &operator=(const shared_ptr<T> &sp)
  113.  
    {
  114.  
    _ptr = sp.get();
  115.  
    return *this;
  116.  
    }
  117.  
     
  118.  
    T &operator*()
  119.  
    {
  120.  
    return *_ptr;
  121.  
    }
  122.  
    T *operator->()
  123.  
    {
  124.  
    return _ptr;
  125.  
    }
  126.  
     
  127.  
    private:
  128.  
    T *_ptr;
  129.  
    };
  130.  
     
  131.  
    }
  132.  
     
  133.  
    int main()
  134.  
    {
  135.  
     
  136.  
    myspace::shared_ptr<int> sp1(new int);
  137.  
    myspace::shared_ptr<int> sp2(sp1);
  138.  
    myspace::shared_ptr<int> sp3(sp1);
  139.  
     
  140.  
     
  141.  
    myspace::shared_ptr<int> sp4(new int);
  142.  
    myspace::shared_ptr<int> sp5(sp4);
  143.  
     
  144.  
    sp1=sp2;
  145.  
     
  146.  
    *sp2=3;
  147.  
     
  148.  
     
  149.  
     
  150.  
     
  151.  
     
  152.  
     
  153.  
     
  154.  
    system("pause");
  155.  
    return 0;
  156.  
    }
学新通
问题:
// shared_ptr 智能指针是线程安全的吗?
// 是的,引用计数的加减是加锁保护的。但是指向资源不是线程安全的
// 指向堆上资源的线程安全问题是访问的人处理的,智能指针不管,也管不了
// 引用计数的线程安全问题,是智能指针要处理的
  • std::shared_ptr的线程安全问题
通过下面的程序我们来测试 shared_ptr 的线程安全问题。需要注意的是 shared_ptr 的线程安全分
为两方面:
1. 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时
-- ,这个操作不是原子的,引用计数原来是 1 了两次,可能还是 2. 这样引用计数就错
乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数 -- 是需要加锁
的,也就是说引用计数的操作是线程安全的。
2. 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。
// 1. 演示引用计数线程安全问题,就把 AddRefCount SubRefCount 中的锁去掉
// 2. 演示可能不出现线程安全问题,因为线程安全问题是偶现性问题, main 函数的 n 改大一些概率就
变大了,就容易出现了。
// 3. 下面代码我们使用 SharedPtr 演示,是为了方便演示引用计数的线程安全问题,将代码中的
SharedPtr 换成 shared_ptr 进行测试,可以验证库的 shared_ptr ,发现结论是一样的。
  1.  
    #include <iostream>
  2.  
    #include <vector>
  3.  
    #include <mutex>
  4.  
    #include<thread>
  5.  
    #include <algorithm>
  6.  
    using namespace std;
  7.  
     
  8.  
    namespace myspace
  9.  
    {
  10.  
     
  11.  
    template <class T>
  12.  
    class shared_ptr
  13.  
    {
  14.  
     
  15.  
    public:
  16.  
    shared_ptr(T *ptr = nullptr) : _ptr(ptr), _pRefCount(new int(1)), _pmtx(new mutex)
  17.  
     
  18.  
    {
  19.  
    }
  20.  
     
  21.  
    shared_ptr(const shared_ptr<T> &sp) : _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx)
  22.  
    {
  23.  
    Addref();
  24.  
    }
  25.  
     
  26.  
    void Addref()
  27.  
    {
  28.  
    // _pmtx->lock();
  29.  
    (*_pRefCount);
  30.  
    // _pmtx->unlock();
  31.  
    }
  32.  
     
  33.  
    void Release()
  34.  
    {
  35.  
     
  36.  
    _pmtx->lock();
  37.  
    bool flag = false;
  38.  
     
  39.  
    if (--(*_pRefCount) == 0 && _ptr)
  40.  
    {
  41.  
     
  42.  
    // cout << "delete" << _ptr << endl;
  43.  
    delete _ptr;
  44.  
    delete _pRefCount;
  45.  
    flag = true;
  46.  
    }
  47.  
     
  48.  
    _pmtx->unlock();
  49.  
    if (flag == true)
  50.  
    {
  51.  
    delete _pmtx;
  52.  
    }
  53.  
    }
  54.  
     
  55.  
    shared_ptr<T> &operator=(const shared_ptr<T> &sp)
  56.  
    {
  57.  
     
  58.  
    if (_ptr != sp._ptr)
  59.  
    {
  60.  
    Release();
  61.  
    _ptr = sp._ptr;
  62.  
    _pRefCount = sp._pRefCount;
  63.  
    _pmtx = sp._pmtx;
  64.  
    Addref();
  65.  
    }
  66.  
    return *this;
  67.  
    }
  68.  
     
  69.  
    int use_count()
  70.  
    {
  71.  
    return *_pRefCount;
  72.  
    }
  73.  
     
  74.  
    ~shared_ptr()
  75.  
    {
  76.  
    Release();
  77.  
    }
  78.  
     
  79.  
    T &operator*()
  80.  
    {
  81.  
    return *_ptr;
  82.  
    }
  83.  
     
  84.  
    T *operator->()
  85.  
    {
  86.  
    return _ptr;
  87.  
    }
  88.  
     
  89.  
    T *get() const
  90.  
    {
  91.  
    return _ptr;
  92.  
    }
  93.  
     
  94.  
    private:
  95.  
    T *_ptr;
  96.  
    int *_pRefCount;
  97.  
    mutex *_pmtx;
  98.  
    };
  99.  
     
  100.  
    template <class T>
  101.  
    class weak_ptr
  102.  
    {
  103.  
     
  104.  
    public:
  105.  
    weak_ptr() : _ptr(nullptr)
  106.  
    {
  107.  
    }
  108.  
     
  109.  
    weak_ptr(const shared_ptr<T> &sp) : _ptr(sp.get())
  110.  
    {
  111.  
    }
  112.  
     
  113.  
    weak_ptr<T> &operator=(const shared_ptr<T> &sp)
  114.  
    {
  115.  
    _ptr = sp.get();
  116.  
    return *this;
  117.  
    }
  118.  
     
  119.  
    T &operator*()
  120.  
    {
  121.  
    return *_ptr;
  122.  
    }
  123.  
    T *operator->()
  124.  
    {
  125.  
    return _ptr;
  126.  
    }
  127.  
     
  128.  
    private:
  129.  
    T *_ptr;
  130.  
    };
  131.  
     
  132.  
    }
  133.  
     
  134.  
    struct Date
  135.  
    {
  136.  
    int _year = 0;
  137.  
    int _month = 0;
  138.  
    int _day = 0;
  139.  
    };
  140.  
     
  141.  
    void SharePtrFunc(myspace::shared_ptr<Date> &sp, size_t n, mutex &mtx)
  142.  
    {
  143.  
     
  144.  
    cout << sp.get() << endl;
  145.  
     
  146.  
    for (size_t i = 0; i < n; i)
  147.  
    {
  148.  
     
  149.  
    // 这里智能指针拷贝会 计数,智能指针析构会--计数,这里是线程安全的。
  150.  
    myspace::shared_ptr<Date> copy(sp);
  151.  
     
  152.  
    // 这里智能指针访问管理的资源,不是线程安全的。所以我们看看这些值两个线程 了2n
  153.  
    // 次,但是最终看到的结果,并一定是加了2n
  154.  
    {
  155.  
    unique_lock<mutex> lk(mtx);
  156.  
     
  157.  
    copy->_year ;
  158.  
    copy->_month ;
  159.  
    copy->_day ;
  160.  
     
  161.  
     
  162.  
    }
  163.  
    }
  164.  
    }
  165.  
     
  166.  
    void test02(){
  167.  
     
  168.  
    myspace::shared_ptr<Date> p(new Date);
  169.  
     
  170.  
    cout<<p.get()<<endl;
  171.  
     
  172.  
    const size_t n=100000;
  173.  
    mutex mtx;
  174.  
     
  175.  
    thread t1(SharePtrFunc,ref(p),n,ref(mtx));
  176.  
    thread t2(SharePtrFunc,ref(p),n,ref(mtx));
  177.  
     
  178.  
     
  179.  
    t1.join();
  180.  
    t2.join();
  181.  
     
  182.  
    cout<<p->_year<<endl;
  183.  
    cout<<p->_month<<endl;
  184.  
    cout<<p->_day<<endl;
  185.  
     
  186.  
    cout<<p.use_count()<<endl;
  187.  
     
  188.  
     
  189.  
    }
  190.  
     
  191.  
    int main()
  192.  
    {
  193.  
     
  194.  
    test02();
  195.  
    cout<<"hello"<<endl;
  196.  
     
  197.  
    // myspace::shared_ptr<int> sp1(new int);
  198.  
    // myspace::shared_ptr<int> sp2(sp1);
  199.  
    // myspace::shared_ptr<int> sp3(sp1);
  200.  
    // myspace::shared_ptr<int> sp4(new int);
  201.  
    // myspace::shared_ptr<int> sp5(sp4);
  202.  
    // sp1 = sp2;
  203.  
    // *sp2 = 3;
  204.  
     
  205.  
    system("pause");
  206.  
    return 0;
  207.  
    }
学新通
std::shared_ptr 的循环引用
循环引用分析:
1. node1 node2 两个智能指针对象指向两个节点,引用计数变成 1 ,我们不需要手动delete。
2. node1 _next 指向 node2 node2 _prev 指向 node1 ,引用计数变成 2
3. node1 node2 析构,引用计数减到 1 ,但是 _next 还指向下一个节点。但是 _prev 还指向上一个节点。
4. 也就是说 _next 析构了, node2 就释放了。
5. 也就是说 _prev 析构了, node1 就释放了。
6. 但是 _next 属于 node 的成员, node1 释放了, _next 才会析构,而 node1 _prev 管理, _prev
属于 node2 成员,所以这就叫循环引用,谁也不会释放。
  1.  
    #include <iostream>
  2.  
    #include <vector>
  3.  
    #include <mutex>
  4.  
    #include<thread>
  5.  
    #include <algorithm>
  6.  
    using namespace std;
  7.  
     
  8.  
    namespace myspace
  9.  
    {
  10.  
     
  11.  
    template <class T>
  12.  
    class shared_ptr
  13.  
    {
  14.  
     
  15.  
    public:
  16.  
    shared_ptr(T *ptr = nullptr) : _ptr(ptr), _pRefCount(new int(1)), _pmtx(new mutex)
  17.  
     
  18.  
    {
  19.  
    }
  20.  
     
  21.  
    shared_ptr(const shared_ptr<T> &sp) : _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx)
  22.  
    {
  23.  
    Addref();
  24.  
    }
  25.  
     
  26.  
    void Addref()
  27.  
    {
  28.  
    _pmtx->lock();
  29.  
    (*_pRefCount);
  30.  
    _pmtx->unlock();
  31.  
    }
  32.  
     
  33.  
    void Release()
  34.  
    {
  35.  
     
  36.  
    _pmtx->lock();
  37.  
    bool flag = false;
  38.  
     
  39.  
    if (--(*_pRefCount) == 0 && _ptr)
  40.  
    {
  41.  
     
  42.  
    // cout << "delete" << _ptr << endl;
  43.  
    delete _ptr;
  44.  
    delete _pRefCount;
  45.  
    flag = true;
  46.  
    }
  47.  
     
  48.  
    _pmtx->unlock();
  49.  
    if (flag == true)
  50.  
    {
  51.  
    delete _pmtx;
  52.  
    }
  53.  
    }
  54.  
     
  55.  
    shared_ptr<T> &operator=(const shared_ptr<T> &sp)
  56.  
    {
  57.  
     
  58.  
    if (_ptr != sp._ptr)
  59.  
    {
  60.  
    Release();
  61.  
    _ptr = sp._ptr;
  62.  
    _pRefCount = sp._pRefCount;
  63.  
    _pmtx = sp._pmtx;
  64.  
    Addref();
  65.  
    }
  66.  
    return *this;
  67.  
    }
  68.  
     
  69.  
    int use_count()
  70.  
    {
  71.  
    return *_pRefCount;
  72.  
    }
  73.  
     
  74.  
    ~shared_ptr()
  75.  
    {
  76.  
    Release();
  77.  
    }
  78.  
     
  79.  
    T &operator*()
  80.  
    {
  81.  
    return *_ptr;
  82.  
    }
  83.  
     
  84.  
    T *operator->()
  85.  
    {
  86.  
    return _ptr;
  87.  
    }
  88.  
     
  89.  
    T *get() const
  90.  
    {
  91.  
    return _ptr;
  92.  
    }
  93.  
     
  94.  
    private:
  95.  
    T *_ptr;
  96.  
    int *_pRefCount;
  97.  
    mutex *_pmtx;
  98.  
    };
  99.  
     
  100.  
    template <class T>
  101.  
    class weak_ptr
  102.  
    {
  103.  
     
  104.  
    public:
  105.  
    weak_ptr() : _ptr(nullptr)
  106.  
    {
  107.  
    }
  108.  
     
  109.  
    weak_ptr(const shared_ptr<T> &sp) : _ptr(sp.get())
  110.  
    {
  111.  
    }
  112.  
     
  113.  
    weak_ptr<T> &operator=(const shared_ptr<T> &sp)
  114.  
    {
  115.  
    _ptr = sp.get();
  116.  
    return *this;
  117.  
    }
  118.  
     
  119.  
    T &operator*()
  120.  
    {
  121.  
    return *_ptr;
  122.  
    }
  123.  
    T *operator->()
  124.  
    {
  125.  
    return _ptr;
  126.  
    }
  127.  
     
  128.  
    private:
  129.  
    T *_ptr;
  130.  
    };
  131.  
     
  132.  
    }
  133.  
     
  134.  
    struct Date
  135.  
    {
  136.  
    int _year = 0;
  137.  
    int _month = 0;
  138.  
    int _day = 0;
  139.  
    };
  140.  
     
  141.  
    void SharePtrFunc(myspace::shared_ptr<Date> &sp, size_t n, mutex &mtx)
  142.  
    {
  143.  
     
  144.  
    cout << sp.get() << endl;
  145.  
     
  146.  
    for (size_t i = 0; i < n; i)
  147.  
    {
  148.  
     
  149.  
    // 这里智能指针拷贝会 计数,智能指针析构会--计数,这里是线程安全的。
  150.  
    myspace::shared_ptr<Date> copy(sp);
  151.  
     
  152.  
    // 这里智能指针访问管理的资源,不是线程安全的。所以我们看看这些值两个线程 了2n
  153.  
    // 次,但是最终看到的结果,并一定是加了2n
  154.  
    {
  155.  
    unique_lock<mutex> lk(mtx);
  156.  
     
  157.  
    copy->_year ;
  158.  
    copy->_month ;
  159.  
    copy->_day ;
  160.  
     
  161.  
     
  162.  
    }
  163.  
    }
  164.  
    }
  165.  
     
  166.  
    void test02(){
  167.  
     
  168.  
    myspace::shared_ptr<Date> p(new Date);
  169.  
     
  170.  
    cout<<p.get()<<endl;
  171.  
     
  172.  
    const size_t n=100000;
  173.  
    mutex mtx;
  174.  
     
  175.  
    thread t1(SharePtrFunc,ref(p),n,ref(mtx));
  176.  
    thread t2(SharePtrFunc,ref(p),n,ref(mtx));
  177.  
     
  178.  
     
  179.  
    t1.join();
  180.  
    t2.join();
  181.  
     
  182.  
    cout<<p->_year<<endl;
  183.  
    cout<<p->_month<<endl;
  184.  
    cout<<p->_day<<endl;
  185.  
     
  186.  
    cout<<p.use_count()<<endl;
  187.  
     
  188.  
     
  189.  
    }
  190.  
     
  191.  
     
  192.  
    struct ListNode
  193.  
    {
  194.  
    int _data;
  195.  
    shared_ptr<ListNode> _next;
  196.  
    shared_ptr<ListNode> _prev;
  197.  
    ~ListNode(){
  198.  
    cout<<"~ListNode()"<<endl;
  199.  
     
  200.  
    }
  201.  
    };
  202.  
     
  203.  
    void test03(){
  204.  
     
  205.  
    std:: shared_ptr<ListNode> node1(new ListNode);
  206.  
    std:: shared_ptr<ListNode> node2(new ListNode);
  207.  
     
  208.  
    cout<<node1.use_count()<<endl;
  209.  
    cout<<node2.use_count()<<endl;
  210.  
     
  211.  
    node1->_next=node2;
  212.  
    node2->_prev=node1;
  213.  
     
  214.  
    cout<<node1.use_count()<<endl;
  215.  
    cout<<node2.use_count()<<endl;
  216.  
     
  217.  
     
  218.  
    }
  219.  
     
  220.  
    int main()
  221.  
    {
  222.  
     
  223.  
    // test02();
  224.  
    test03();
  225.  
    cout<<"hello"<<endl;
  226.  
     
  227.  
    // myspace::shared_ptr<int> sp1(new int);
  228.  
    // myspace::shared_ptr<int> sp2(sp1);
  229.  
    // myspace::shared_ptr<int> sp3(sp1);
  230.  
    // myspace::shared_ptr<int> sp4(new int);
  231.  
    // myspace::shared_ptr<int> sp5(sp4);
  232.  
    // sp1 = sp2;
  233.  
    // *sp2 = 3;
  234.  
     
  235.  
    system("pause");
  236.  
    return 0;
  237.  
    }
学新通

学新通

 学新通

// 解决方案:在引用计数的场景下,把节点中的 _prev _next 改成 weak_ptr 就可以了
// 原理就是, node1->_next = node2; node2->_prev = node1; weak_ptr _next
_prev 不会增加 node1 node2 的引用计数。
  1.  
    #include<iostream>
  2.  
    #include<vector>
  3.  
    #include<algorithm>
  4.  
    using namespace std;
  5.  
    #include<memory>
  6.  
    #include<thread>
  7.  
     
  8.  
     
  9.  
    struct ListNode
  10.  
    {
  11.  
    int _data;
  12.  
    weak_ptr<ListNode> _next;
  13.  
    weak_ptr<ListNode> _prev;
  14.  
     
  15.  
    ~ListNode(){
  16.  
    cout<<"~ListNode()"<<endl;
  17.  
    }
  18.  
     
  19.  
     
  20.  
     
  21.  
    };
  22.  
     
  23.  
    void test01(){
  24.  
     
  25.  
    shared_ptr<ListNode> node1(new ListNode);
  26.  
    shared_ptr<ListNode> node2(new ListNode);
  27.  
     
  28.  
     
  29.  
    cout<<node1.use_count()<<endl;
  30.  
    cout<<node2.use_count()<<endl;
  31.  
     
  32.  
    node1->_next=node2;
  33.  
    node2->_prev=node1;
  34.  
     
  35.  
    cout<<node1.use_count()<<endl;
  36.  
    cout<<node2.use_count()<<endl;
  37.  
     
  38.  
     
  39.  
    }
  40.  
     
  41.  
     
  42.  
     
  43.  
     
  44.  
     
  45.  
     
  46.  
    int main()
  47.  
    {
  48.  
     
  49.  
     
  50.  
    test01();
  51.  
     
  52.  
     
  53.  
    system("pause");
  54.  
    return 0;
  55.  
    }
学新通
运行结果
学新通
如果不是 new 出来的对象如何通过智能指针管理呢?其实 shared_ptr 设计了一个删除器来解决这
个问题( ps :删除器这个问题我们了解一下)
  1.  
    #include<iostream>
  2.  
    #include<vector>
  3.  
    #include<algorithm>
  4.  
    using namespace std;
  5.  
    #include<memory>
  6.  
    #include<thread>
  7.  
     
  8.  
     
  9.  
    struct ListNode
  10.  
    {
  11.  
    int _data;
  12.  
    weak_ptr<ListNode> _next;
  13.  
    weak_ptr<ListNode> _prev;
  14.  
     
  15.  
    ~ListNode(){
  16.  
    cout<<"~ListNode()"<<endl;
  17.  
    }
  18.  
     
  19.  
     
  20.  
     
  21.  
    };
  22.  
     
  23.  
    void test01(){
  24.  
     
  25.  
    shared_ptr<ListNode> node1(new ListNode);
  26.  
    shared_ptr<ListNode> node2(new ListNode);
  27.  
     
  28.  
     
  29.  
    cout<<node1.use_count()<<endl;
  30.  
    cout<<node2.use_count()<<endl;
  31.  
     
  32.  
    node1->_next=node2;
  33.  
    node2->_prev=node1;
  34.  
     
  35.  
    cout<<node1.use_count()<<endl;
  36.  
    cout<<node2.use_count()<<endl;
  37.  
     
  38.  
     
  39.  
    }
  40.  
     
  41.  
     
  42.  
    //仿函数的删除器
  43.  
    template<class T>
  44.  
    struct FreeFunc
  45.  
    {
  46.  
    void operator()(T* ptr){
  47.  
    cout<<"free:"<<endl;
  48.  
    free(ptr);
  49.  
    }
  50.  
     
  51.  
    };
  52.  
     
  53.  
    template<class T>
  54.  
     
  55.  
    struct DeleteArrayFunc{
  56.  
     
  57.  
    void operator()(T *ptr){
  58.  
    cout<<"delete[]"<<ptr<<endl;
  59.  
    delete [] ptr;
  60.  
    }
  61.  
     
  62.  
     
  63.  
    };
  64.  
    struct A{
  65.  
     
  66.  
    };
  67.  
     
  68.  
    void test02(){
  69.  
     
  70.  
    FreeFunc<int> freeFunc;
  71.  
    shared_ptr<int> sp1((int *)malloc(4),freeFunc);
  72.  
     
  73.  
    DeleteArrayFunc<int> deleteArrayFunc;
  74.  
    shared_ptr<int> sp2( (int *)malloc(4),deleteArrayFunc);
  75.  
     
  76.  
    shared_ptr<A> sp4(new A[10],[](A *p){delete[]p;});
  77.  
    shared_ptr<FILE> sp5(fopen("test.txt","w"),[](FILE *p){
  78.  
    fclose(p);
  79.  
    });
  80.  
     
  81.  
     
  82.  
    }
  83.  
     
  84.  
     
  85.  
     
  86.  
     
  87.  
     
  88.  
    int main()
  89.  
    {
  90.  
     
  91.  
     
  92.  
    // test01();
  93.  
    test02();
  94.  
     
  95.  
     
  96.  
    system("pause");
  97.  
    return 0;
  98.  
    }
学新通

4.C 11boost中智能指针的关系

1. C 98 中产生了第一个智能指针 auto_ptr.
2. C boost 给出了更实用的 scoped_ptr shared_ptr weak_ptr.
3. C TR1 ,引入了 shared_ptr 等。不过注意的是 TR1 并不是标准版。
4. C 11 ,引入了 unique_ptr shared_ptr weak_ptr 。需要注意的是 unique_ptr 对应 boost
scoped_ptr 。并且这些智能指针的实现原理是参考 boost 中的实现的。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhiafikb
系列文章
更多 icon
同类精品
更多 icon
继续加载