天天看點

《C++程式設計規範:101條規則、準則與最佳實踐》——2.9 確定資源為對象所擁有。使用顯式的RAII和智能指針

本節書摘來自異步社群出版社《c++程式設計規範:101條規則、準則與最佳實踐》一書中的第2章,第2.9節,作者:【加】herb sutter , 【羅】andrei,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

摘要

利器在手,不要再徒手為之:c++的“資源擷取即初始化”(resource acquisition is initialization,raii)慣用法是正确處理資源的利器。raii使編譯器能夠提供強大且自動的保證,這在其他語言中可是需要脆弱的手工編寫的慣用法才能實作的。配置設定原始資源的時候,應該立即将其傳遞給屬主對象。永遠不要在一條語句中配置設定一個以上的資源。

讨論

c++語言所強制施行的構造函數/析構函數對稱反映了資源擷取/釋放函數對比如fopen/fclose、lock/unlock和new/delete的本質的對稱性。這使具有資源擷取的構造函數和具有資源釋放的析構函數的基于棧(或引用計數)的對象成為了自動化資源管理和清除的極佳工具。

這種自動化很容易實作、簡潔、低成本而且天生防錯。如果不予采用,就需要手工将調用正确配對,包括存在分支控制流和異常的情形,這可是很不容易而且需要注意力高度集中的任務。既然c++已經通過易用的raii提供了如此直接的自動化,這種c語言式的仍然依賴于對資源解除配置設定的微觀管理方式就是不可接受的了。

每當處理需要配對的擷取/釋放函數調用的資源時,都應該将資源封裝在一個對象中,讓對象為我們強制配對,并在其析構函數中執行資源釋放。例如,我們無需直接調用一對非成員函數 openport/closeport,而是可以考慮如下方法:

void fun( shared_ptr sp1, shared_ptr sp2 );

// ……

fun( shared_ptr(new widget), shared_ptr(new widget) );<code>`</code>`

這種代碼是不安全的。c++标準給了編譯器巨大的回旋餘地,可以将構成函數兩個參數的兩個表達式重新排序。說得更具體一些,就是編譯器可以交叉執行兩個表達式:可能先執行兩個對象的記憶體配置設定(通過調用operator new),然後再試圖調用兩個widget構造函數。這恰恰為資源洩漏準備了溫床,因為如果其中一個構造函數調用抛出異常的話,另一個對象的記憶體就永遠也沒有機會釋放了!(詳細情況請參閱 [sutter02]。)

這種微妙的問題有一個簡單的解決辦法:遵循建議,絕對不要在一條語句中配置設定一個以上的資源,應該在自己的代碼語句中執行顯式的資源配置設定(比如new),而且每次都應該馬上将配置設定的資源賦予管理對象(比如shared_ptr)。例如:

繼續閱讀