從C++的RAII理解智能指針的思路(一)

Contact me:

Blog : cugtyt.github.io/blog/i

Email: cugtyt#qq.com, cugtyt#gmail.com


  • 從C++的RAII理解智能指針的思路(一)
  • 從C++的RAII理解智能指針的思路(二)

假設我們有如下代碼:

class MyClass;void fun() { MyClass mc; // do sth. with mc}

fun函數結束的時候mc還存在嗎?當然,這是c++的基本知識,我們創建mc的時候調用構造函數,離開作用域就調用析構函數,所以mc已經不存在了。

void fun() { int* iarr = new int[10]; // do sth. wirh iarr}

fun函數結束的時候iarr還在嗎?當然,iarr不在了,可是它申請的空間還是沒有釋放,這造成了內存泄漏。

在C++中,這種在生命周期結束時釋放資源的方法被稱作資源獲取即初始化(Resource Acquisition Is Initialization (RAII))。我們能不能把這個特性用到資源管理上呢?

class Unique {public: Unique(int n) { std::cout << "Unique" << std::endl; ptr = new int[n]; } ~Unique() { std::cout << "~Unique" << std::endl; delete []ptr; } // other funcprivate: int *ptr;};int main() { Unique u1(10); // do sth. with u}Output: Unique ~Unique

這樣一個最為粗糙和簡陋的管理方法就出現了,在函數結束的時候內存也釋放了,當然我們拿int數組作為例子。很好,但是只能適用簡單情況,很多問題不能處理,比如,我們不能隨意把ptr暴露出來。如果外面獲取了ptr,把內存釋放掉了,那麼等我們釋放的時候就是第二次釋放了,會出問題的。同樣也不能實現內存共享,對象複製也受到影響等。

Unique u1{10};Unique u2 = u1; // !!!Output: Unique ~Unique ~Unique

上面的代碼會造成兩個指針同時指向一片區域,調用構造函數就二次釋放了,我們需要避免它。我們可以簡單粗暴的把賦值構造函數和拷貝構造函數delete就可以解決意外複製的情況:

class Unique {public:... Unique(const Unique&) = delete; Unique& operator=(const Unique&) = delete;...}

這樣,沒有了拷貝,如果我們為了需要轉移所有權,可以在函數裡面寫入對指針的判斷:

class Unique {public: Unique(int n) { std::cout << "Unique" << std::endl; ptr = new int[n]; } Unique(const Unique& u) { this->ptr = u.ptr; u.ptr = std::nullptr; } Unique& operator=(const Unique&) {/*與上面函數一樣*/} ~Unique() { std::cout << "~Unique" << std::endl; if (ptr) { delete []ptr; } } // other funcprivate: int *ptr;};int main() { Unique u1(10); Unique u2(u1);}

u1Unique,我們希望轉移所有權給u2,然後u1就不能繼續使用了。

雖然兩次析構函數都會調用,但是內存正確釋放一次,我們實現了unique_ptr的基本思路。注意這是模型,僅供理解,和c++內部實現並不一樣。

那麼shared_ptrweak_ptr呢,後面我們繼續討論。

推薦閱讀:

如何向完全不懂編程的小夥伴解釋「程序寫死」?
C++ 中 cout 是個對象,包含頭文件後可以直接用,那麼它是在哪裡定義的呢?
25歲了,從未接觸過編程,還可以把編程作為愛好培養嗎?
為什麼C++的庫函數的定義會這麼複雜?
如何優化一個讀取命令並執行的程序?

TAG:C | 智能指針 | 內存管理 |