C++基於模板實現智慧指標

真昼小天使daisuki發表於2024-08-30

某廠面試,當時反正是沒寫出來,估計是寄了,事後做個記錄。

#include <iostream>
#include <mutex>
using namespace std;

class ObjectElement {
private:
    char *addr;
    int size;
    void release() {
        addr = nullptr;
        size = 0;
    }

public:
    ObjectElement(int size) {
        cout<< "construct: "<<this<<endl;
        this->size = size;
        addr = new char[size];
    }

    ~ObjectElement() {
        cout<< "release: "<<this<<endl;
        delete []addr;
        release();
    }

    void PrintTest() {
        cout<<"ObjectElement"<<endl;
    }

};


// 注意不是執行緒安全的,需要加鎖

template <typename T>
class SharedPtr {
private:
    // 可以用一個額外的ControlBlock 結構儲存以下兩個私有變數,此處只需要一個指標,做不做兩可
    T *ptr;
    int *ref_count; // 核心,如果直接用int:每個shared_ptr 就會有自己的引用計數器,它們不會同步更新

    void release() { // private
        if(ref_count != nullptr && --(*ref_count) == 0) {
            delete ptr;
            delete ref_count;
        }
    }

public:

    SharedPtr() : ptr(nullptr), ref_count(nullptr) {}

    explicit SharedPtr(T *p) {
        ptr = p;
        ref_count = new int(1);
    }

    // 注意只有在複製或者賦值的時候才會有引用計數的增加

    // 複製構造,增加引用計數
    SharedPtr(const SharedPtr &s) noexcept {
        ptr = s.ptr;
        ref_count = s.ref_count;
        if(ref_count != nullptr) {
            (*ref_count)++;
        }
    }

    // 移動構造,沒有增加引用計數
    SharedPtr(SharedPtr &&s) noexcept {
        ptr = s.ptr;
        ref_count = s.ref_count;
        s.ptr = nullptr;
        s.ref_count = nullptr;
    }

    // 複製賦值運算子,與複製構造類似,但是需要先判斷是否是自己,注意返回自身引用
    SharedPtr& operator=(const SharedPtr &s) noexcept{
        if(this == &s) {
            release(); // 重要!刪除原有的

            ptr = s.ptr;
            ref_count = s.ref_count;
            if(ref_count != nullptr) {
                (*ref_count)++;
            }
        }
        return *this;
    }

    // 移動賦值運算子,同理
    SharedPtr& operator=(SharedPtr &&s) noexcept{
        if(this == &s) {
            release(); // 重要!刪除原有的
            
            ptr = s.ptr;
            ref_count = s.ref_count;
            s.ptr = nullptr;
            s.ref_count = nullptr;
        }
        return *this;
    }

    // 解引用,過載*,返回T物件
    T& operator*() const {
        return *ptr;
    }

    // 過載指標運算子,使得可以類似->呼叫
    T* operator->() const{
        return ptr;
    }

    // 獲取裸指標,同上
    T* get() const{
        return ptr;
    }

    int use_count() const {
        return (ref_count!=nullptr?(*ref_count):0);
    }

    void reset(T* p = nullptr) {
        release();

        ptr = p;
        if(p == nullptr) {
            ref_count = nullptr;
        }
        else {
            ref_count = new int(1);
        }
    }

    ~SharedPtr() {
        release();
    }


};





int main() {

    ObjectElement *obj = new ObjectElement(100);

    SharedPtr<ObjectElement> sptr1(obj);
    SharedPtr<ObjectElement> sptr2 = sptr1;

    cout<<sptr1.use_count()<<endl;
    cout<<sptr2.use_count()<<endl;

    (*sptr1).PrintTest();
    sptr1->PrintTest();
    sptr1.get()->PrintTest();


    sptr1.reset();

    cout<<sptr1.use_count()<<endl;
    cout<<sptr2.use_count()<<endl;


    return 0;
}

預期輸出為:

相關文章