C++智能指针的基本实现

1. 代码实现

智能指针核心逻辑:

  • 构造函数或者赋值时,增加或调整引用计数
  • 析构函数时,减小引用计数,如果引用计数为0,就释放内存。
  • 麻烦的地方是使用场景以及各种参数类型,要考虑全面。

这里仅考虑单线程场景,多线程使用需要再完善。

#pragma once

#define SAFE_DELETE(pObj)   \
    {                       \
        if (pObj) {         \
            delete pObj;    \
            pObj = NULL;    \
        }                   \
    }


namespace Yu {

template <class Tclass SmartPtr {
public:
    /* 指针作为构造函数参数
     * 例如:
     * int* n2 = new int(6);
     * Yu::SmartPtr<int> p4(n2);
     */

    explicit SmartPtr(T* pObj = NULL) : m_pObj(pObj)m_pRef(new int(1)) {}

    // 析构函数调用ReleaseRef减少引用计数
    // 引用计数为0时,会自动释放指针
    ~SmartPtr()
    {
        ReleaseRef();
    }

    /* 拷贝构造函数
     * 例如:
     * Yu::SmartPtr<int> p1;
     * Yu::SmartPtr<int> p2(p1);
     */

    SmartPtr(const SmartPtr& r) : m_pObj(r.m_pObj), m_pRef(r.m_pRef)
    {
        AddRef();
    }

    /* =操作符重载
     * 例如:
     * Yu::SmartPtr<int> p3;
     * p3 = p2;
     */

    SmartPtr& operator=(const SmartPtr& r)
    {
        // 赋值语句左右两边是不同对象,才需要处理
        if (this != &r) {
            // 首先释放对原来指针的引用
            ReleaseRef();

            m_pRef = r.m_pRef;
            m_pObj = r.m_pObj;
            AddRef();
        }

        return *this;
    }

    /* =操作符重载
     * 例如:
     * int* n1 = new int(5);
     * Yu::SmartPtr<int> p1;
     * p1 = n1;
     */

    SmartPtr& operator=(T* r)
    {
        // 首先释放对原来指针的引用
        ReleaseRef();

        // 已经管理一个新的指针
        // 引用计数应该重新new一个,不要再使用之前的。
        m_pRef = new int(1);
        m_pObj = r;

        return *this;
    }

    /* ->操作符重载
     * 例如:
     * auto arr = new std::vector<int>(10);
     * Yu::SmartPtr<std::vector<int>> p1(arr);
     * std::cout << p1->size() << std::endl;
     */

    T* operator->() const
    {
        return m_pObj;
    }

    /* *操作符重载
     * 例如:
     * int* n1 = new int(1);
     * Yu::SmartPtr<int> p1(n1);
     * *p1 = 2;
     */

    T& operator*() const
    {
        return *m_pObj;
    }

    /* !操作符重载
     * 支持bool语境判断指针是否为空
     * 例如:
     * Yu::SmartPtr<int> p1;
     * if (!p1) {
     *     XXXX;
     * }
     */

    bool operator!() const
    {
        return !m_pObj;
    }

    /* ==操作符重载
     * 例如:
     * Yu::SmartPtr<int> p1;
     * Yu::SmartPtr<int> p2;
     * if (p1 == p2) {
     *     XXXX;
     * }
     */

    bool operator==(const SmartPtr<T>& r) const
    {
        return m_pObj == r.m_pObj;
    }

    /* !=操作符重载
     * 例如:
     * Yu::SmartPtr<int> p1;
     * Yu::SmartPtr<int> p2;
     * if (p1 != p2) {
     *     XXXX;
     * }
     */

    bool operator!=(const SmartPtr<T>& r) const
    {
        return m_pObj != r.m_pObj;
    }


    /* 支持bool语境判断指针是否为非空
     * 例如:
     * Yu::SmartPtr<int> p1;
     * if (p1) {
     *     XXXX;
     * }
     */

    typedef T* SmartPtr<T>::*unspecified_bool_type;

    operator unspecified_bool_type() const
    
{
        return !m_pObj ? 0 : &SmartPtr<T>::m_pObj;
    }

    // 获取原始指针
    T* Get() const
    
{
        return m_pObj;
    }

    // 获取当前指针引用计数
    int GetRef() const
    
{
        return *m_pRef;
    }

private:
    // 增加指针引用计数
    void AddRef()
    
{
        ++*m_pRef;
    }

    // 减小指针引用计数
    // 如果引用计数为0,释放指针
    virtual void ReleaseRef()
    
{
        --*m_pRef;
        if (*m_pRef <= 0) {
            SAFE_DELETE(m_pRef);
            SAFE_DELETE(m_pObj);
        }
    }

private:
    T* m_pObj;
    // 多个SmartPtr要共享引用计数,所以引用计数也要用指针
    int* m_pRef;
};

// namespace Yu

代码开源地址:https://gitee.com/ferrisyu/CodingSea/blob/master/Common/smart_ptr.h

2. gtest用例

TEST_F(TestSmartPtr, test0)
{
    Yu::SmartPtr<int> p1;
    EXPECT_EQ(p1.Get(), nullptr);
    EXPECT_EQ(p1.GetRef(), 1);

    int* n1 = new int(5);

    // T*赋值运算符
    p1 = n1;

    // 拷贝构造函数
    Yu::SmartPtr<intp2(p1);

    Yu::SmartPtr<int> p3;
    // SmartPtr赋值运算符
    p3 = p2;

    // 此时p1, p2, p3 ---> n1
    EXPECT_EQ(p3.Get(), n1);
    EXPECT_EQ(p3.GetRef(), 3);

    // *运算符
    EXPECT_EQ(*p3, *n1);

    // !运算符
    EXPECT_EQ(!p3, false);

    // unspecified_bool_type运算符
    bool isNull = true;
    if (p3) {
        isNull = false;
    }
    EXPECT_EQ(isNull, false);

    // ==运算符
    EXPECT_EQ(p1 == p3, true);
    
    // T*构造函数
    int* n2 = new int(6);
    Yu::SmartPtr<intp4(n2);
    p3 = p4;
    p4 = nullptr;

    // 此时p1, p2 ---> n1, p3 ---> n2, p4 ---> nullptr
    EXPECT_EQ(p2.GetRef(), 2);
    EXPECT_EQ(*p2, *n1);

    EXPECT_EQ(p3.GetRef(), 1);
    EXPECT_EQ(*p3, *n2);

    EXPECT_EQ(p4, nullptr);
}

代码开源地址:https://gitee.com/ferrisyu/CodingSea/blob/master/Test/TestCommon/test_smartptr.cpp

建站  ·  开发  ·  读书  ·  教程
↑长按或扫码关注“编程之海”公众号↑
↑长按或扫码关注“编程之海”公众号↑
0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论

0

0

227

0
希望看到您的想法,请您发表评论x