智能指针仿真-003-共享智能指针

1. 概述
本篇对共享所有权的智能指针进行仿真。
仿真指针主要有:boost/c++ 11 tr1中的shared_ptr、weak_ptr。
boost与c++ 11 tr1的关系这里稍微做一下类比,shared_ptr在boost中时可以认为是储君(准标准),进入到了tr1之后就正名了就算正式登基了是完全标准的了。

2. this指针的探讨
this指针是编译器默认生成到成员函数中的,类型为T* const this。
例如写一个简单的成员函数。

class A
{
public:
    void func(){}
};

编译器处理起来如下。

void func(A* const this);

接下来要思考的问题就是。
1) this指针可以删除吗?
2) 执行删除操作有引发哪些问题?

2.1. delete this源码
成员函数内进行delete this操作源码如下。

class CTest
{
public:
    CTest() : m_nVal(0){}
    ~CTest() {}

    /// @brief 释放自身内存
    void Release() {delete this;}

private:
    int m_nVal;///< 测试成员变量 };

2.2. delete this栈检测
对于栈分配,进行delete检测。

void TestStackDelete()
{
    /// @brief 检测栈释放
    CTest test;
    test.Release();
}

int _tmain(int argc, _TCHAR* argv[])
{
    TestStackDelete();
    return 0;
}

运行程序会报错。

2.3. delete this堆检测
对于堆分配,进行delete检测。

void TestHeapDelete()
{
    /// @brief 检测堆释放
    CTest* pTest = new CTest;
    pTest->Release();
}

int _tmain(int argc, _TCHAR* argv[])
{
    TestHeapDelete();
    return 0;
}

运行程序后释放内存正常。

2.4. delete this总结
对delete this做下总结。
1) 对栈上分配的空间则不要调用。
2) 可以采用这种方式来释放堆中分配内存。
3) 调用对应接口后,指针将变为野指针,不可再进行后续操作。

3. shared_ptr仿真

3.1. shared_ptr相关问题
带着如下问题来做仿真实现。
1) shared_ptr实现原理?
2) shared_ptr是否就意味着一统天下?
3) shared_ptr使用中的坑?

3.2. 引用计数类仿真
Shared_ptr中采用引用计数来进行所有权共享,当引用计数为0时进行资源释放。首先进行引用计数类仿真MyRefCount.h。

#pragma once

/// @brief 引用计数类
template<class _Ty>
class CMyRefCount
{
public:
    CMyRefCount(_Ty *_Px)
        : m_nUses(1)
        , m_nWeaks(1)
        , _Ptr(_Px)
    {}

    /// @brief 增加强引用计数
    void _Incref() {++m_nUses;}
    /// @brief 增加弱引用计数
    void _Incwref() {++m_nWeaks;}
    /// @brief 减少强引用计数
    void _Decref()
    {
        --m_nUses;
        if (0 == m_nUses)
        {
            _Destroy();
            _Decwref();
        }
    }
    /// @brief 减少弱引用计数
    void _Decwref()
    {
        --m_nWeaks;
        if (0 == m_nWeaks)
        {
            _Delete_this();
        }
    }
    /// @brief 获取强引用计数
    long _Use_count() const {return (m_nUses);}
    /// @brief 指针是否已释放
    bool _Expired() const {return (0==m_nUses);}

private:
    void _Destroy() {delete _Ptr;}
    void _Delete_this() {delete this;}

private:
    long m_nUses;///< 强引用次数 long m_nWeaks;///< 弱引用次数 _Ty * _Ptr; };

3.3. 共享指针基类仿真
Weak_ptr和shared_ptr的共用基类进行仿真MyPtrBase.h。

#pragma once
#include "MyRefCount.h"

/// @brief shared_ptr/weak_ptr基类
template<class _Ty>
class CMyPtrBase
{
    typedef CMyPtrBase<_Ty> _Myt;

public:
    /// @brief 获取强引用计数
    long use_count() const
    {
        if (NULL == _Rep) {return 0;}
        return (_Rep->_Use_count());
    }

protected:
    CMyPtrBase()
        : _Ptr(nullptr)
        , _Rep(nullptr)
    {}
    /// @brief 指针是否已释放
    bool _Expired() const
    {
        if (NULL == _Rep) {return true;}
        return (_Rep->_Expired());
    }
    /// @brief 获取原生指针
    _Ty *_Get() const {return (_Ptr);}
    /// @brief 减少强引用计数
    void _Decref()
    {
        if (nullptr == _Rep) {return;}
        _Rep->_Decref();
    }
    /// @brief 重置资源强引用
    void _Reset(const _Myt& _Other)
    {
        _Reset(_Other._Ptr, _Other._Rep, false);
    }
    void _Reset(_Ty *_Other_ptr, CMyRefCount<_Ty> *_Other_rep, bool bInit)
    {
        /// @brief 初次赋值则不做增加操作
        if (!bInit)
        {
            if (_Other_rep != nullptr) {_Other_rep->_Incref();}///< other强引用增加
        }
        if (_Rep != nullptr) {_Rep->_Decref();}///< this强引用减少
        _Rep = _Other_rep;
        _Ptr = _Other_ptr;
    }
    /// @brief 减少弱引用计数
    void _Decwref()
    {
        if (nullptr == _Rep) {return;}
        _Rep->_Decwref();
    }
    /// @brief 重置资源弱引用
    void _Resetw(const _Myt& _Other)
    {
        _Resetw(_Other._Ptr, _Other._Rep);
    }
    void _Resetw(_Ty *_Other_ptr, CMyRefCount<_Ty> *_Other_rep)
    {
        if (_Other_rep != nullptr) {_Other_rep->_Incwref();}///< other弱引用增加
        if (_Rep != nullptr) {_Rep->_Decwref();}///< this弱引用减少
        _Rep = _Other_rep;
        _Ptr = _Other_ptr;
    }

private:
    _Ty *_Ptr; ///< 持有指针
    CMyRefCount<_Ty> *_Rep; ///< 引用计数
};

3.4. weak_ptr及shared_ptr仿真源码
对应实现类MySharedPtr.h。

#pragma once
#include "MyPtrBase.h"

template<class _Ty>
class CMyWeakPtr;
template<class _Ty>
class CMySharedPtr;

template<class _Ty>
class CMySharedPtr : public CMyPtrBase<_Ty>
{
public:
    typedef CMySharedPtr<_Ty> _Myt;

    /// @brief 默认构造函数
    CMySharedPtr() {}
    /// @brief 析构函数中释放资源
    ~CMySharedPtr()
    {
        this->_Decref();
    }
    /// @brief 显示声明传值构造函数
    explicit CMySharedPtr(_Ty *_Px)
    {
        this->_Reset(_Px, new CMyRefCount<_Ty>(_Px), true);
    }
    /// @brief 拷贝构造函数
    CMySharedPtr(const _Myt& _Other)
    {
        this->_Reset(_Other);
    }
    /// @brief 赋值操作符
    _Myt& operator=(const _Myt& _Other)
    {
        this->_Reset(_Other);
        return (*this);
    }
    /// @brief 支持weak_ptr复制
    /// 不支持weak_ptr赋值,需要通过lock来转获取share_ptr
    explicit CMySharedPtr(const CMyWeakPtr<_Ty>& _Other)
    {
        this->_Reset(_Other);
    }
    /// @brief 重置
    void reset()
    {
        this->_Reset(nullptr, nullptr, false);
    }
    /// @brief 获取原生指针
    _Ty *get() const {return (this->_Get());}
    /// @brief 仿真指针操作
    _Ty *operator->() const {   return (this->_Get());}
    _Ty &operator*() const {    return (*this->_Get());}
    /// @brief 是否唯一持有资源
    bool unique() const {return (this->use_count() == 1);}
};

template<class _Ty>
class CMyWeakPtr : public CMyPtrBase<_Ty>
{
    typedef CMyWeakPtr<_Ty> _Myt;

public:
    CMyWeakPtr() {}
    ~CMyWeakPtr() {this->_Decwref();}

    explicit CMyWeakPtr(const _Myt& _Other) {this->_Resetw(_Other);}
    CMyWeakPtr& operator=(const _Myt& _Other)
    {
        this->_Resetw(_Other);
        return (*this);
    }
    /// @brief shared_ptr支持
    explicit CMyWeakPtr(const CMySharedPtr<_Ty>& _Other) {this->_Resetw(_Other);}
    CMyWeakPtr& operator=(const CMySharedPtr<_Ty>& _Other)
    {
        this->_Resetw(_Other);
        return (*this);
    }
    /// @brief 重置
    void reset() {this->_Resetw(nullptr, nullptr, false);}
    /// @brief 资源释放还存在
    bool expired() const {return (this->_Expired());}
    /// @brief 转化为shared_ptr
    CMySharedPtr<_Ty> lock() const
    {
        return (CMySharedPtr<_Ty>(*this));
    }
};

3.5. 仿真指针使用源码
对指针进行使用。

#include "stdafx.h"
#include "MySharedPtr.h"
#include <iostream>

class CTest
{
public:
    CTest() {std::cout<<"CTest()"<<std::endl;}
    ~CTest() {std::cout<<"~CTest()"<<std::endl;}
    void test_print() {std::cout<<"test_print()"<<std::endl;}
};

int _tmain(int argc, _TCHAR* argv[])
{
    CMySharedPtr<CTest> t1(new CTest);
    /// @brief 原生指针检测
    t1->test_print();
    (*t1).test_print();
    std::cout<<t1.use_count()<<std::endl;
    {
        /// @brief 检测复制
        CMySharedPtr<CTest> t2(t1);
        std::cout<<t1.use_count()<<std::endl;
    }
    std::cout<<t1.use_count()<<std::endl;
    {
        /// @brief 检测赋值
        CMySharedPtr<CTest> t3;
        t3 = t1;
        std::cout<<t1.use_count()<<std::endl;
    }
    std::cout<<t1.use_count()<<std::endl;
    {
        /// @brief 检测weak_ptr
        CMyWeakPtr<CTest> w1(t1);///< weak_ptr不会增加引用计数
        std::cout<<t1.use_count()<<std::endl;
        CMySharedPtr<CTest> t4(w1);///< 通过构造函数获取weak_ptr引用指针
        std::cout<<t1.use_count()<<std::endl;
        CMySharedPtr<CTest> t5;
        //t5 = w1;///< 检测weak_ptr直接赋值会编译失败
        t5 = w1.lock();///< 通过lock获取weak_ptr引用指针
        std::cout<<t1.use_count()<<std::endl;
    }

    return 0;
}

输出结果:

CTest()
test_print()
test_print()
1
2
1
2
1
1
2
3
~CTest()

3.6. shared_ptr环状引用示例
shared_ptr出现环状引用的话将出现资源泄漏。采用自引用示例来检测。

#include "stdafx.h"
#include "MySharedPtr.h"
#include <iostream>

class CSelfRefTest
{
public:
    CSelfRefTest() {std::cout<<"CSelfRefTest()"<<std::endl;}
    ~CSelfRefTest() {std::cout<<"~CSelfRefTest()"<<std::endl;}

    CMySharedPtr<CSelfRefTest> m_pTest;
};

int _tmain(int argc, _TCHAR* argv[])
{
    /// @brief shared_ptr产生环形引用时将出现资源泄漏情况
    /// 采用自引用示例来说明
    CMySharedPtr<CSelfRefTest> p1(new CSelfRefTest);
    std::cout<<p1.use_count()<<std::endl;
    p1->m_pTest = p1;///< 进行自引用操作
    std::cout<<p1.use_count()<<std::endl;
    return 0;
}

运行结果:

CSelfRefTest()
1
2

3.7. weak_ptr解决环状引用
weak_ptr引用不会引起计数增加,可用来破环状引用。

#include "MySharedPtr.h"
#include <iostream>

class CSelfRefTest
{
public:
    CSelfRefTest() {std::cout<<"CSelfRefTest()"<<std::endl;}
    ~CSelfRefTest() {std::cout<<"~CSelfRefTest()"<<std::endl;}

    CMyWeakPtr<CSelfRefTest> m_pTest;
};

int _tmain(int argc, _TCHAR* argv[])
{
    /// @brief shared_ptr产生环形引用时将出现资源泄漏情况
    /// 采用weak_ptr来解决该问题
    CMySharedPtr<CSelfRefTest> p1(new CSelfRefTest);
    std::cout<<p1.use_count()<<std::endl;
    p1->m_pTest = p1;///< 进行自引用操作
    std::cout<<p1.use_count()<<std::endl;
    return 0;
}

运行结果:

CSelfRefTest()
1
1
~CSelfRefTest()

3.8. shared_ptr总结
通过仿真源码,对shared_ptr总结如下。
1) 共享型智能指针通过引用计数方式来实现所有权共享,但同时增加了程序复杂度和运行性能。
2) 避免出现环状引用,这将会引起资源泄漏。
3) weak_ptr是shared_ptr的助手,是弱指针,可用于解除环状引用,但不支持原生指针获取以及->和*操作。

你可能感兴趣的:(智能指针,shared-ptr,weak-ptr)