C++ 智能指針shared_ptr/unique_ptr自定義刪除器

默認情況下,智能指針使用delete釋放其管理的資源,有時候,可能要修改默認使用delete釋放資源的行爲。本文將列出我所知道的所有自定義刪除器的方法。

零、引例

Connection是一個管理連接類,在釋放Connection之前,我們需要調用close函數來關閉連接。觀察如下代碼:

#include <iostream>
#include <memory>
#include <string>

using namespace std;

class Connection{
public:
    explicit Connection(string name):_name(name){
    }
    string get_name() const {
        return _name;
    }
private:
    string _name;
};

void close(Connection* connection){
    cout << string("關閉")+connection->get_name()+"管理的連接中..." << endl;
    //關閉連接的代碼
    // .....
    cout << "關閉完成。" << endl;
}

int main(){
    // 新建管理連接Connection的智能指針
    shared_ptr<Connection> sp(new Connection);
    unique_ptr<Connection> up(new Connection);
}

執行上述代碼,發現並沒有辦法調用close函數,因爲控制權完全在shared_ptr/unique_ptr中。你可能會說,在退出作用域之前我調用close(sp.get())先關閉連接,這樣不就可以了嘛?實際上,這種做法對於shared_ptr並不安全,手動close之後,不能確保sp管理的Connection不會繼續使用(即sp中的計數指針多於1)。因此,需要使用自定義的刪除器。

爲了節省篇幅,後面代碼中不在貼出公共的代碼。

一、使用函數

刪除函數定義類似於:

void Deleter(T *val){
    // 其他代碼
    // 釋放val的內存
    delete val;
    // 或者(如果val是數組)
    delete[] val;
}

T是shared_ptr/unique_ptr管理的類型,val是指針,可以指向一個實例,也可以是數組的首地址(參考:使用shared_ptr/unique_ptr管理數組),取決於shared_ptr/unique_ptr管理的具體內容。
原理是:當刪除器的指針Deleter傳給shared_ptr/unique_ptr時,shared_ptr/unique_ptr不會使用默認的delete val來釋放其管理的資源,而是使用Deleter(val)來釋放資源,這樣就調用了Deleter來釋放管理的資源。後面的各種方式的原理也是如此。

// 函數式刪除器
void Deleter(Connection *connection){
    close(connection);
    delete connection;
}

int main(){
    // 新建管理連接Connection的智能指針
    shared_ptr<Connection> sp(new Connection("shared_ptr"), Deleter);
    unique_ptr<Connection, decltype(Deleter)*> up(new Connection("unique_ptr"), Deleter);
}

shared_ptr在使用的時候,只需要把函數式刪除器的指針傳給構造函數就行;而unique_ptr還用增加一個模板參數decltype(Deleter)*,這是shared_ptr和shared_ptr的不同點之一(注意:unique_ptr的第二個模板參數是指針)。

二、使用可調用類

可調用類是指重載了調用運算符的類。可調用的對象的好處是它也是一個類,可以用來保存一些狀態。

class DeleterClass{
public:
    DeleterClass():_count(0){}
    /**
     * 重載調用運算符。
     * 這裏要定義成模板函數,纔可以在unique_ptr中使用。
     */
    template <typename T>
    void operator ()(T *connection){
        close(connection);
        delete connection;
    }
private:
    int _count;
};

int main(){
    // 新建管理連接Connection的智能指針
    shared_ptr<Connection> sp(new Connection("shared_ptr"), DeleterClass());
    DeleterClass dc;
    unique_ptr<Connection, DeleterClass> up(new Connection("unique_ptr"), dc);
    unique_ptr<Connection, DeleterClass> up1(new Connection("unique_ptr2"), up.get_deleter());
}

unique_ptr的第二個模板參數是類類型,構造函數的第二個參數是可調用類的實例。

三、使用lambda表達式

目前最簡單的自定義刪除器(好爽啊,還有如此簡潔的代碼)。

int main(){
    auto DeleterLambda=[](Connection *connection){
        close(connection);
        delete connection;
    };
    // 新建管理連接Connection的智能指針
    shared_ptr<Connection> sp(new Connection("shared_ptr"), DeleterLambda);
    unique_ptr<Connection, decltype(DeleterLambda)> up(new Connection("unique_ptr"), DeleterLambda);
}

四、使用std::function

使用這種形式講真沒有第一種簡單,優勢在於std::function的特點(比如參數綁定等等)。

void Deleter(Connection *connection){
    close(connection);
    delete connection;
}

int main(){
    std::function<void (Connection*)> deleter(Deleter);
    // 新建管理連接Connection的智能指針
    shared_ptr<Connection> sp(new Connection("shared_ptr"), deleter);
    unique_ptr<Connection, decltype(deleter)> up(new Connection("unique_ptr"), deleter);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章