什么是智能指針?為什么需要這個東西?
這需要從C++最早的設計說起,C++為了提高語言的自由程度,允許使用C++的程序員自己控制內存,C++繼承了C語言的指針特性,允許用戶在C++中像C一樣使用指針在堆中開辟一塊較大的內存,但是由於性能的考慮,用戶自己申請的內存需要用戶自己主動釋放。這就導致了有時遺忘釋放時帶來的內存泄漏問題。由此智能指針為了解決這個問題,誕生了。
C++的智能指針從C++11標准開始由boost引入, 關於boost的智能指針在此不再做介紹。
以下討論的內容涉及了C++11-C++20中的技術規則。
在C++中使用智能指針需要包含<memory>頭文件,智能指針作為STL的一部分來提供,所以在使用時需要使用std::命名空間,智能指針標准庫提供了兩個主要類:std::unique_ptr(私有指針)和std::shared_ptr(共享指針)。外加一個輔助類std::weak_ptr,下面一一介紹:
1.私有指針std::unique_ptr
std::unique_ptr是一個模板類,所以在使用的時候需要輸入一個類型參數,並且標准庫提供了一個內存申請函數用來替代new。std::make_unique.
1 #include<memory> 2 3 int main() { 4 5 std::unique_ptr<int> p = std::make_unique<int>(12); 6 7 // 類似於 int*p = new int(12); 8 return 0; 9 }
當私有智能智能指針超出當前作用域時,智能指針將釋放自己持有的內存。也就是說,上面的p指針,在main函數返回時,釋放所持有的內存。智能指針也可以搭配new來完成內存的申請。
1 #include<memory> 2 3 int main() { 4 5 6 std::unique_ptr<int> p2{ new int(13) }; 7 8 return 0; 9 }
此時,在p2超出作用域時,依然可以自動釋放所持有的內存。
私有指針是不允許被復制的。
以下語法是錯誤的:
1 #include<memory> 2 3 int main() { 4 5 6 std::unique_ptr<int> p2{ new int(13) }; 7 8 std::unique_ptr<int> m = p2; // 錯誤,不允許被復制 9 10 return 0; 11 }
但是,可以被移動:
1 #include<memory> 2 3 int main() { 4 5 6 std::unique_ptr<int> p2{ new int(13) }; 7 8 std::unique_ptr<int> m = std::move(p2); 9 10 return 0; 11 }
私有指針要求,每一塊內存只能有一個對象持有。所以符合移動復制的都是合法的,符合拷貝復制的都是不合法的。
對於智能指針數組,標准庫依然有一套特化來解決。
1 #include<memory> 2 3 int main() { 4 5 6 std::unique_ptr<int[]> p = std::make_unique<int[]>(12); 7 8 return 0; 9 }
上面的代碼中表示,申請了一塊數組長度為12 int類型的連續內存。數組指針也可以配合new來完成,以達到數組的初始化工作。
1 #include<memory> 2 3 int main() { 4 5 6 std::unique_ptr<int[]> p{ new int[12]{12,23,45,67} }; 7 8 return 0; 9 }
2.共享指針std::shared_ptr
共享指針的使用方式和私有指針相似,但是共享指針是允許被復制的。
1 #include<memory> 2 3 int main() { 4 5 6 std::shared_ptr<int> p = std::make_shared<int>(12); 7 8 auto s = p; // ok 9 10 return 0; 11 }
共享指針的釋放條件是:當所有持有該內存的指針對象全部消亡以后,釋放該內存。
對於智能指針訪問內部數據的方法是,該對象重載了*和->操作符。
1 #include<memory> 2 3 int main() { 4 5 6 std::shared_ptr<int> p = std::make_shared<int>(12); 7 8 int m = *p; // 讀取p的內存值 9 10 int* s = p.get(); // 獲取p的內存的地址 11 12 return 0; 13 }
私有智能指針允許將內存管理權限交於程序員自己管理。
1 #include<memory> 2 3 int main() { 4 5 6 auto p = std::make_unique<int>(12); 7 8 int* s = p.release(); // 此時智能指針對象在消亡時,不再主動釋放內存。 9 10 return 0; 11 }
共享指針由於存在可能被多個對象持有的情況,所以不支持內存權限轉移。
3. std::waek_ptr
1 #include<memory> 2 3 int main() { 4 5 6 auto p = std::make_shared<int>(12); 7 8 std::weak_ptr<int> wk(p); 9 if (!wk.lock()) { 10 // 對象已經釋放。 11 } 12 wk.reset(); // 釋放持有的智能指針對象。(注意:不是釋放持有的智能指針的內存,而是釋放持有的p對象的引用。) 13 14 return 0; 15 }
std::waek_ptr在使用時的主要用途是用來判斷一個共享指針是否已經釋放,對於智能指針的原理,每個人都有自己的一套見解,對於智能指針的原理,這就涉及到源碼解析,由於篇幅過大,在此不再細說。
有幾點是需要注意的:
1.盡量避免智能指針與普通指針的混用。(你要是想騷,那也沒辦法)。
2.避免重寫智能指針的內存管理類。(同上)。
3.在團隊項目上應與團隊成員商議,是否采用智能指針。
智能指針的出現,是為了更方便,而不是為了更騷,更復雜,一切都應該建立在:此方法能夠讓我更舒服的且高效的工作。