天天看點

C++ 智能指針(unique_ptr / shared_ptr)代碼實作

文章目錄

  • ​​unique_ptr 智能指針的實作​​
  • ​​shared_ptr 智能指針的實作​​
  • ​​指針類型轉換​​

unique_ptr 智能指針的實作

一個對象隻能被單個unique_ptr 所擁有。

#include <iostream>

using namespace std;

/*增加模闆類型,保證智能指針的類型是由傳入的類型決定的*/
template <typename T>

class smart_ptr {
public:
    explicit smart_ptr(T *ptr = nullptr): ptr_(ptr){}
    
    /*移動構造函數,目前智能指針僅能被一個對象引用*/
    smart_ptr(smart_ptr&& other) {ptr_ = other.release();}
    
    /*如果我們想要消除移動構造和拷貝構造,隻需要将其 構造标記為delete*/
    //smart_ptr(const smart_ptr&) = delete;
    //smart_ptr& operator=(const smart_ptr&) = delete;
    
    /*在構造參數時直接生成新的智能指針,進而不再需要在函數體中構造臨時對象,目前支援移動構造進行函數對象的構造。*/
    smart_ptr& operator=(smart_ptr rhs) {
        rhs.swap(*this);
        return *this;
    }   
    
    T* release() {
        T* ptr = ptr_;
        ptr_ = nullptr;
        return ptr;
    }

    void swap(smart_ptr& rhs) {
        using std::swap;//标準模闆庫的交換函數
        swap(rhs.ptr_,ptr_);
    }

    ~smart_ptr(){delete ptr_;}
    T* get() const{ return ptr_;}
    
    /*指針的特性,可以通過*解引用通路 */
    T& operator*() const{return *ptr_;}
    /*指針的特性,可以通過-> 通路指針的位址内容*/
    T* operator->() const{return ptr_;}
    /*重載bool運算符,能夠讓smart_ptr像指針一樣用在布爾表達式中*/
    operator bool() const{return ptr_;}

private:
    T* ptr_;
};

class Shape{
    public:
    virtual void create_shape() = 0;
    virtual ~Shape(){}
};

class Circle: public Shape{
    public:
    Circle(){cout << "Circle::Circle()" << endl;}
    void create_shape(){cout << "create shape: Circle" << endl;}
    ~Circle(){cout << "Circle::delete()" << endl;}
};

class Triangle: public Shape{
    public:
    Triangle(){cout << "Triangle::Tirangle()" << endl;}
    void create_shape(){cout << "create shape: Triangle" << endl;}
    ~Triangle(){cout << "Triangle::delete()" << endl;}
};

int main()
{
    smart_ptr<Shape> ptr1(new Circle);
    smart_ptr<Shape> ptr2(nullptr);
    smart_ptr<Shape> ptr3 ;
    
    //ptr3 = ptr1; //編譯報錯,指派需要一個對象(而非引用),因而進入執行之前需要引發一個構造,但是此時沒有可用的構造函數
     
    ptr3 = std::move(ptr2); // 編譯正常,支援移動構造 
    
    return 0;
}      

shared_ptr 智能指針的實作

多個shared_ptr可以共享同一個對象,當他們全部失效的時候,這個對象才會被删除。

此時對shared_ptr的需求是共享 同一個對象時也需要共享同一個計數,當最後一個指向對象(和共享計數)的shared_ptr析構時,它需要删除對象和共享計數。

在以上unique_ptr的實作基礎上增加引用計數相關的操作,實作如下:

#include <iostream>

using namespace std;

/*增加一個引用計數類,來記錄目前對象被智能指針引用的次數*/
class shared_count{
public:
    shared_count():count_(1) {}
    
    void add_count(){
        ++count_;
    }
    
    long reduce_count(){
        return --count_;
    }

    long get_count() {
        return count_;
    }
private:
    long count_;
};

/*增加類模闆,保證智能指針的類型是由傳入的類型決定的*/
template <typename T>

class smart_ptr {
public:
    explicit smart_ptr(T *ptr = nullptr): ptr_(ptr){
        if(ptr) {
            shared_count_ = new shared_count();
        }
    }
    
    /*移動構造函數,目前智能指針僅能被一個對象引用,同時不進行引用計數的自加*/
    template <typename U>
    smart_ptr(smart_ptr<U>&& other) {
        ptr_ = other.release();
        if(ptr_) {
            shared_count_ = other.shared_count_;
            other.ptr_ = nullptr;
        }
    }

    /*拷貝構造函數,支援子類對象向父類對象的拷貝*/
    template <typename U>
    smart_ptr(const smart_ptr<U>& other) {
        ptr_ = other.ptr_;
        if(ptr_) {
            other.shared_count_ -> add_count();
            shared_count_ = other.shared_count_;
        }
    }

    /*同類型對象的拷貝構造函數*/
    smart_ptr(smart_ptr & other) {
        ptr_ = other.ptr_;
        if(ptr_) {
            other.shared_count_ -> add_count();
            shared_count_ = other.shared_count_;
        }
    }

    /*在構造參數時直接生成新的智能指針,進而不再需要在函數體中構造臨時對象,目前支援移動構造進行函數對象的構造。*/
    smart_ptr& operator=(smart_ptr rhs) {
        rhs.swap(*this);
        return *this;
    }   
    
    T* release() {
        T* ptr = ptr_;
        ptr_ = nullptr;
        return ptr;
    }

    void swap(smart_ptr& rhs) {
        using std::swap;
        swap(rhs.ptr_,ptr_);
        swap(rhs.shared_count_,shared_count_);
    }
    
    long use_count() const {
        if(ptr_) {
            return shared_count_->get_count();
        } else {
            return 0;
        }
    }

    ~smart_ptr(){
    //  cout << "smart_ptr::delete count is " << shared_count_ -> get_count() << endl;
        if(ptr_ && !shared_count_ -> reduce_count()){
            delete ptr_;
            delete shared_count_;
        }
    }
    
    T* get() const{ return ptr_;}
    T& operator*() const{return *ptr_;}
    T* operator->() const{return ptr_;}
    operator bool() const{return ptr_;}

private:
    T* ptr_;
    shared_count* shared_count_;//統計對象引用計數的類
};


class Shape{
    public:
    virtual void create_shape() = 0;
    virtual ~Shape(){}
};

class Circle: public Shape{
    public:
    Circle(){cout << "Circle::Circle()" << endl;}
    void create_shape(){cout << "create shape: Circle" << endl;}
    ~Circle(){cout << "Circle::delete()" << endl;}
};

class Triangle: public Shape{
    public:
    Triangle(){cout << "Triangle::Tirangle()" << endl;}
    void create_shape(){cout << "create shape: Triangle" << endl;}
    ~Triangle(){cout << "Triangle::delete()" << endl;}
};

int main()
{
    smart_ptr<Shape> ptr1(new Circle);
    cout << "use count of ptr1 is: " << ptr1.use_count() << endl;
    
    smart_ptr<Shape> ptr2;
    cout << "use count of ptr2 was: " << ptr2.use_count() << endl;
    
    //ptr2 = std::move(ptr1); // 移動拷貝構造,不進行計數增加(可檢視以上的實作)
    ptr2 = ptr1; //普通的拷貝構造,支援多個指針共享同一個對象,則對應智能指針的共享計數增加
    cout << "use count of ptr2 is now: " << ptr2.use_count() << endl;
    
    if(ptr1) {
        cout << "ptr1 is not empty " << endl;   
    }
    return 0;
}      

輸出如下:

Circle::Circle()
use count of ptr1 is: 1
use count of ptr2 was: 0
use count of ptr2 is now: 2
Circle::delete()      

指針類型轉換

​​C++已有的強制類型轉換​​有如下幾種:

  • static_cast 用于類層次結構中基類和派生類之間指針或引用的轉換
  • reinterpret_cast 改變指針或引用的類型、将指針或引用轉換為一個足夠長度的整形、将整型轉換為指針或引用類型
  • const_cast 用于強制去掉不能被修改的常數特性
  • dynamic_cast dynamic_cast是運作時處理的,運作時要進行類型檢查。

智能指針需要實作類似的函數模闆,想要達到以上對應的強制類型轉換的功能,我們需要增加構造函數,且允許在對智能指針内部的指針對象指派時,使用一個現有的智能指針的共享計數。如下所示:

template <typename U>
smart_ptr(const smart_ptr<U> &other, T* ptr) { //拷貝構造時,使用T* ptr進行類型轉換
    _ptr = ptr;
    if(_ptr) {
        other.shared_count_ -> add_count();
        shared_count_ = other.shared_count_;
    }
}      

根據以上代碼,實作​

​dynamic_pointer_cast​

template <typename T, typename U>
smart_ptr <T> dynamic_pointer_cast(
    const smart_ptr<U> &other) {
    T *ptr = dynamic_cast<T*> (other.get());
    return smart_ptr<T> (other,ptr);
}      

使用方式如下:

template <typename T, typename U>
smart_ptr <T> static_pointer_cast(
    const smart_ptr<U> &other) {
    T *ptr = static_cast<T*> (other.get());
    return smart_ptr<T> (other,ptr);
}

template <typename T, typename U>
smart_ptr <T> reinterpret_pointer_cast(
    const smart_ptr<U> &other) {
    T *ptr = reinterpret_cast<T*> (other.get());
    return smart_ptr<T> (other,ptr);
}

template <typename T, typename U>
smart_ptr <T> const_pointer_cast(
    const smart_ptr<U> &other) {
    T *ptr = const_cast<T*> (other.get());
    return smart_ptr<T> (other,ptr);
}      

繼續閱讀