程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++侵入式智能指針的實現

C++侵入式智能指針的實現

編輯:關於C++

簡介

在現代C++編程中,智能指針給我們在資源管理上帶來了很多好處,這裡就不多說了。

在工作中,我們常常會用智能指針來管理資源,其中最常用的就是引用計數類智能指針了(shared_ptr)。

資源共享型的智能指針有兩種實現,一種是侵入式,一種是非侵入式。

在教材裡比較常見的是非侵入式的,它的實現完全放在智能指針模板裡,模板類有一個用於保存資源類對象的指針變量,和一個用於記錄資源對象使用計數的指針變量,這兩個東西是所有的智能指針對象共享的,所以通過指針保存。

這裡寫圖片描述

而侵入式則不同,它的實現分散在智能指針模板和使用智能指針模板的類中:模板類只有一個用於保存對象的指針變量,對象的計數放在了資源類中。

這裡寫圖片描述vc+436OsyrnTw8TatObSsrHIvc/J2aOs0rKxyL3PsLLIq6O7PC9wPg0KPHA+MqGi0vLOqtL908O8xsr9tOa0otTattTP87G+ye2jrMv50tTU2rqvyv2199PDtcTKsbryv8nS1NaxvdO0q7Xd18rUtLbUz/O12Na3o6y2+LK708O1o9DE0v3Tw7zGyv3WtbaqyqejqLfHx9bI68q91sfE3Na41eu21M/ztcS/vbG0o6yx2NDrtPjXxdbHxNzWuNXrxKOw5aOst/HU8r7Nu+Gz9s/WttTP89L908O8xsr9tqrKp6OpoaM8L3A+DQo8cD7IsbXjysejujwvcD4NCjxwPjGhotfK1LTA4LHY0OvT0NL908O8xsr9seTBv6OssqLH0rjDseTBv7XE1Pa89b/J0tSxu8fWyOvKvdbHxNzWuNXrxKOw5bv5wOCy2df3o6zV4s/UtcPC6bezo7s8L3A+DQo8cD4yoaLI57n7uMPA4LKisrvP68q508PWx8Tc1rjV66Osy/y7ucrHu+G0+NfF0v3Tw7zGyv2x5MG/oaM8L3A+DQo8cD7B7c3io6zWx8Tc1rjV69PQ0ru49s7et6ix3MPitcTOyszio6y+zcrH0a27t9L908OhozwvcD4NCjxoMiBpZD0="侵入式智能指針實現">侵入式智能指針實現

兩個要點:

1.將引用計數變量從資源類中抽離出來,封裝成一個基類,該基類包含了引用計數變量。如果一個類想使用智能指針,則只需要繼承自該基類即可;

2.引用計數的基類,設計成模板類,接受引用計數類型作為參數,比如使用int類型或者原子計數類型作為引用計數變量。默認情況下應該使用原子計數類型作為引用計數變量。

3.引用計數基類的構造函數、拷貝構造函數、析構函數應為protected,即該類只能通過繼承關系被使用。

4.拷貝構造函數並不拷貝引用計數基類的數據成員,而是重新將原子計數_atomic置為0——因為每個對象都有一個自己的引用計數,當發生對象拷貝構造時,新的對象的計數應該置為0,而不應該拷貝舊對象的計數。

5.賦值操作operator=,比如A=B,同上面一樣,只對資源類的成員進行拷貝,而不拷貝其引用計數基類的數據成員。也就是說,將B的值賦給A,A的引用計數應該保持不變,而不能將B的引用計數拷貝過來——這是對象的拷貝,而不是智能指針的拷貝。

首先實現引用計數基類(注:Atomic的實現這裡就不給出了):

/**
 *  @brief 智能指針基類.
 *  
 *  所有需要智能指針支持的類都需要從該對象繼承,
 *  
 *  內部采用引用計數Atomic實現,對象可以放在容器中;
 */

template
class HandleBaseT
{
public:

     /** 原子計數類型*/
    typedef T atomic_type;

    /**
     * @brief 復制。引用計數不能被復制。
     *
     * @return HandleBase&
     */
    HandleBaseT& operator=(const HandleBaseT&)
    {
        return *this;
    }

    /**
     * @brief 增加計數
     */
    void incRef() { _atomic.inc_fast(); }

    /**
     * @brief 減少計數, 當計數==0時, 且需要刪除數據時, 釋放對象
     */
    void decRef()
    {
        if(_atomic.dec_and_test() && !_bNoDelete)
        {
            _bNoDelete = true;
            delete this;
        }
    }

    /**
     * @brief 獲取計數.
     *
     * @return int 計數值
     */
    int getRef() const        { return _atomic.get(); }

    /**
     * @brief 設置不自動釋放. 
     *  
     * @param b 是否自動刪除,true or false
     */
    void setNoDelete(bool b)  { _bNoDelete = b; }

protected:

    /**
     * @brief 構造函數
     */
    HandleBaseT() : _atomic(0), _bNoDelete(false)
    {
    }

    /**
     * @brief 拷貝構造,_atomic和_bNoDelete不能被拷貝,只能重置
     */
    HandleBaseT(const HandleBaseT&) : _atomic(0), _bNoDelete(false)
    {
    }

    /**
     * @brief 析構
     */
    virtual ~HandleBaseT()
    {
    }

protected:

    /**
     * 計數
     */
    atomic_type   _atomic;

    /**
     * 是否自動刪除
     */
    bool        _bNoDelete;
};

// 針對int類型計數變量的特化
// 在類聲明中定義的函數將自動inline,類外定義的函數需顯式inline
template<>
inline void HandleBaseT::incRef() 
{ 
    ++_atomic; 
}

template<> 
inline void HandleBaseT::decRef()
{
    if(--_atomic == 0 && !_bNoDelete)
    {
        _bNoDelete = true;
        delete this;
    }
}

template<> 
inline int HandleBaseT::getRef() const        
{ 
    return _atomic; 
} 

// 默認使用Atomic作為引用計數類型
typedef HandleBaseT HandleBase;

以上實現了計數基類,所有需要使用智能指針的對象必須繼承自該類。

智能指針模板類的實現需要關注的幾個點:

1.初始化、賦值等操作需要考慮參數是原始指針、其他類型的智能指針初、用同一類型的智能指針三種情況。

2.需要重載<、=、!=幾個操作(非成員函數),左右操作數使用兩個模板參數。

3.賦值操作需要檢查自我賦值。

/**
* @brief 空指針異常
*/
struct Shared_PtrNull_Exception : public Exception
{
    Shared_PtrNull_Exception(const string &buffer) : Exception(buffer){};
    ~Shared_PtrNull_Exception() throw(){};
};

/**
 * @brief 智能指針模板類. 
 *  
 * 可以放在容器中,且線程安全的智能指針. 
 *  
 * 通過它定義智能指針,該智能指針通過引用計數實現, 
 *  
 * 可以放在容器中傳遞. 
 *  
 * template T必須繼承於HandleBase 
 */
template
class Shared_Ptr
{
public:

    /**
     * 元素類型
     */
    typedef T element_type;

    /**
     * @brief 用原生指針初始化, 計數+1. 
     *  
     * @param p
     */
    Shared_Ptr(T* p = 0)
    {
        _ptr = p;

        if(_ptr)
        {
            _ptr->incRef();
        }
    }

    /**
     * @brief 用其他智能指針r的原生指針初始化, 計數+1. 
     *  
     * @param Y
     * @param r
     */
    template
    Shared_Ptr(const Shared_Ptr& r)
    {
        _ptr = r._ptr;

        if(_ptr)
        {
            _ptr->incRef();
        }
    }

    /**
     * @brief 拷貝構造, 計數+1. 
     *  
     * @param r
     */
    Shared_Ptr(const Shared_Ptr& r)
    {
        _ptr = r._ptr;

        if(_ptr)
        {
            _ptr->incRef();
        }
    }

    /**
     * @brief 析構
     */
    ~Shared_Ptr()
    {
        if(_ptr)
        {
            _ptr->decRef();
        }
    }

    /**
     * @brief 賦值, 普通指針. 
     *  
     * @param p 
     * @return Shared_Ptr&
     */
    Shared_Ptr& operator=(T* p)
    {
        if(_ptr != p)
        {
            if(p)
            {
                p->incRef();
            }

            T* ptr = _ptr;
            _ptr = p;

            if(ptr)
            {
                ptr->decRef();
            }
        }
        return *this;
    }

    /**
     * @brief 賦值, 其他類型智能指針. 
     *  
     * @param Y
     * @param r 
     * @return Shared_Ptr&
     */
    template
    Shared_Ptr& operator=(const Shared_Ptr& r)
    {
        if(_ptr != r._ptr)
        {
            if(r._ptr)
            {
                r._ptr->incRef();
            }

            T* ptr = _ptr;
            _ptr = r._ptr;

            if(ptr)
            {
                ptr->decRef();
            }
        }
        return *this;
    }

    /**
     * @brief 賦值, 該類型其他執政指針. 
     *  
     * @param r 
     * @return Shared_Ptr&
     */
    Shared_Ptr& operator=(const Shared_Ptr& r)
    {
        if(_ptr != r._ptr)
        {
            if(r._ptr)
            {
                r._ptr->incRef();
            }

            T* ptr = _ptr;
            _ptr = r._ptr;

            if(ptr)
            {
                ptr->decRef();
            }
        }
        return *this;
    }

    /**
     * @brief 將其他類型的智能指針換成當前類型的智能指針. 
     *  
     * @param Y
     * @param r 
     * @return Shared_Ptr
     */
    template
    static Shared_Ptr dynamicCast(const Shared_Ptr& r)
    {
        return Shared_Ptr(dynamic_cast(r._ptr));
    }

    /**
     * @brief 將其他原生類型的指針轉換成當前類型的智能指針. 
     *  
     * @param Y
     * @param p 
     * @return Shared_Ptr
     */
    template
    static Shared_Ptr dynamicCast(Y* p)
    {
        return Shared_Ptr(dynamic_cast(p));
    }

    /**
     * @brief 獲取原生指針.
     *
     * @return T*
     */
    T* get() const
    {
        return _ptr;
    }

    /**
     * @brief 調用.
     *
     * @return T*
     */
    T* operator->() const
    {
        if(!_ptr)
        {
            throwNullHandleException();
        }

        return _ptr;
    }

    /**
     * @brief 引用.
     *
     * @return T&
     */
    T& operator*() const
    {
        if(!_ptr)
        {
            throwNullHandleException();
        }

        return *_ptr;
    }

    /**
     * @brief 是否有效.
     *
     * @return bool
     */
    operator bool() const
    {
        return _ptr ? true : false;
    }

    /**
     * @brief  交換指針. 
     *  
     * @param other
     */
    void swap(Shared_Ptr& other)
    {
        std::swap(_ptr, other._ptr);
    }

protected:

    /**
     * @brief 拋出異常
     */
    void throwNullHandleException() const;

public:
    T*          _ptr;

};

/**
 * @brief 拋出異常. 
 *  
 * @param T
 * @param file
 * @param line
 */
template inline void
Shared_Ptr::throwNullHandleException() const
{
    throw Shared_PtrNull_Exception("shared_ptr null handle error");
}

/**
 * @brief ==判斷. 
 *  
 * @param T
 * @param U
 * @param lhs
 * @param rhs
 *
 * @return bool
 */
template
inline bool operator==(const Shared_Ptr& lhs, const Shared_Ptr& rhs)
{
    T* l = lhs.get();
    U* r = rhs.get();
    if(l && r)
    {
        return *l == *r;
    }
    else
    {
        return !l && !r;
    }
}

/**
 * @brief 不等於判斷. 
 *  
 * @param T
 * @param U
 * @param lhs
 * @param rhs
 *
 * @return bool
 */
template
inline bool operator!=(const Shared_Ptr& lhs, const Shared_Ptr& rhs)
{
    T* l = lhs.get();
    U* r = rhs.get();
    if(l && r)
    {
        return *l != *r;
    }
    else
    {
        return l || r;
    }
}

/**
 * @brief 小於判斷, 用於放在map等容器中. 
 *  
 * @param T
 * @param U
 * @param lhs
 * @param rhs
 *
 * @return bool
 */
template
inline bool operator<(const Shared_Ptr& lhs, const Shared_Ptr& rhs)
{
    T* l = lhs.get();
    U* r = rhs.get();
    if(l && r)
    {
        return *l < *r;
    }
    else
    {
        return !l && r;
    }
}

使用示例

class Test: public HandleBase
{
    // ...
};

Shared_Ptr myTestClass = new Test();
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved