默認情況下,智能指針使用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);
}