類中使用shared_ptr()的問題
當我們先定義一個指標,然後再用這個指標構造兩個智慧指標
int main()
{
int* pt = new int();
std::shared_ptr<int> p1(pt);
std::shared_ptr<int> p2(pt);
std::cout << "p1.use_count() = " << p1.use_count() << std::endl;
std::cout << "p2.use_count() = " << p2.use_count() << std::endl;
return 0;
}
執行後就會報錯,顯示的是pt指標被重複釋放了
原因是p1和p2都以為自己是唯一獨佔pt的智慧指標,不知道還有智慧指標指向pt
所以輸出後發現兩個引用計數都是1
如果需要不報錯,就得這樣寫
shared_ptr<int> p2 = p1
透過p1來定義p2,它們就知道pt有兩個智慧指標了,就不會報錯。
再來看一個程式碼
class client
{
public:
~client()
{
std::cout << "~client()\n";
}
};
int main()
{
client* cp = new client();
std::shared_ptr<client> csp1(cp);
std::shared_ptr<client> csp2(cp);
std::cout << "csp1.use_count: " << csp1.use_count() << std::endl;
std::cout << "csp2.use_count: " << csp2.use_count() << std::endl;
return 0;
}
這個報的一樣的錯,原理相同,問題是我們實際開發中,很多時候需要透過this指標來獲取物件的內容
這個時候需要透過enable_shared_from_this來解決問題
enable_shared_from_this的使用
class client : public std::enable_shared_from_this<client>
{
public:
~client()
{
std::cout << "~client()\n";
}
std::shared_ptr<client> get_ptr()
{
return shared_from_this();
}
};
int main()
{
client* cp = new client();
std::shared_ptr<client> csp1(cp);
std::shared_ptr<client> csp2 = cp->get_ptr();
std::cout << "csp1.use_count: " << csp1.use_count() << std::endl;
std::cout << "csp2.use_count: " << csp2.use_count() << std::endl;
return 0;
}
將程式碼改寫成這樣,先公有繼承這個模板類。
這裡需要注意,在你透過shared_from_this()返回一個類的shared_ptr時,該物件必須已經被一個shared_ptr所管理,所以你不能直接csp2 = cp->get_ptr()
,要在此之前先有csp1(cp)
。
這樣的話,藉助shared_from_this(),可以使得該物件只要引用計數不為零,就任意獲取它的一個shared_ptr。只要還有shared_ptr持有它,它就不會消亡。
實際開發中應用,以一個伺服器demo舉例
首先看下面一段程式碼
struct client : std::enable_shared_from_this<client>
{
public:
void start()
{
}
//...其他函式
}
void start()
{
std::shared_ptr<client> s = std::make_shared<client>();
s->start();
}
int main()
{
start();
return 0;
}
這裡用make_shared初始化了一個client的shared_ptr,make_shared會讓物件和控制塊可以安全地儲存在連續的記憶體塊中。它簡化了記憶體管理,並提高了效能。但是不支援自己寫刪除器。
start是一個初始的函式,裡面會稍後新增業務,下面我們寫一個定時器。
public:
void start()
{
start_up();
}
private:
void start_up()
{
_timer.expires_from_now(std::chrono::seconds(10));
_timer.async_wait(std::bind(&client::time_out, shared_from_this()));
}
void time_out()
{
start_up();
}
private:
boost::asio::steady_timer _timer;
在類裡面這樣設計定時器,當start()呼叫的時候,會呼叫start_up()函式設定一個定時器,並且註冊time_out()這個回撥函式。
此時start()函式呼叫結束了,臨時變數s的智慧指標也已經釋放,但是,定時器內透過呼叫shared_from_this(),返回了一個s管理的物件的shared_ptr給async_wait裡的回撥time_out()中,s管理的物件並未消亡,直到執行完回撥time_out(),它才會消亡,但是回撥裡面如果繼續呼叫start_up()重新設定計時器,便又會返回一個該物件的shared_ptr()傳入新註冊的回撥time_out()內,以此類推,只要計時器不關閉,永遠不會消亡。
基於這一點,可以和讀寫搭配起來,靈活控制當前類在什麼條件下保活,什麼條件下析構。