摘要:在我以為我和緣分尚淺的時候,搬來救兵,智能指針橫空出世,打敗了內存泄漏,拯救了我們的關系。智能指針引入了智能指針的概念,使用了引用計數的想法,讓程序員不再需要關心手動釋放內存。它還增加了一個成員函數用于交換兩個智能指針的值。
內容較多,建議收藏,以后復習
對于程序員來說,如果我不需要,或者我沒那么需要,再或者說有替代品,我都不會再去實現一個新東西。
廢話,程序員哪里有勤快的!
既然它被發明出來,一定是解決問題的!
在之前,我愚昧的小白生涯中,見到復雜的指針,時常倆股戰戰,因為搞不懂什么值傳遞、地址傳遞、實參、形參、引用、一級指針、多級指針、數組指針、函數指針等等概念,
但是仗著膽子大,就直接上手,唉,似乎沒有那么難?
什么動態申請內存,指針賦值,輸出字符串,手到擒來,展示!
char *ch = (char*)malloc(12);memcpy(ch, "Hello World", 12);std::cout << ch << std::endl;
在我的黑框框編程生涯中,它是沒有打擊我的,總是非常給面子的輸出正確答案!
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Lyu9xOL1-1630808532107)(C:/Users/11073/AppData/Roaming/Typora/typora-user-images/image-20210904170337518.png)]
那時候,我以為我和C++的故事會一直這么美好!
那么問題來了?是誰打破了這一美好的幻境!
大學期間,上課走神的我,上完了《C++程序設計》之后,都沒有聽過這個“內存泄漏”,也正是我對他的不熟悉,打破了我和C++穩固的關系,(程序:“呵,我不是能運行就行?”)
什么是內存泄漏?
內存泄漏(Memory Leak)是指程序中已動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。以上定義來自于百度,并且十分準確。
什么!我之前忘記使用free()竟然會造成這么大的后果!
但是我已經形成習慣,已經很想記住,但總是不經意間會忘記釋放內存,畢竟“手動”,一定不是最佳方案。這里有一個思想:RAII 資源獲取即初始化技術:對于一個對象而言,我們在構造函數的時候申請空間,而在析構函數(在 離開作用域時調用)的時候釋放空間。
在我以為我和C++緣分尚淺的時候,C++11搬來救兵,“智能指針”橫空出世,打敗了內存泄漏,拯救了我們的關系。
C++11 引入了智能指針的概念,使用了引用計數的想法,讓程序 員不再需要關心手動釋放內存。也就是說,原本需要我們手動就一一對應的malloc/free或new/delete的工作由智能指針來代替了!
這種自動化,程序員最愛!
這些智能指針就包括 std::shared_ptr/std::unique_ptr/std::weak_ptr, 使用它們需要包含頭文件 。
unique_ptr 是一個獨享所有權的智能指針,它提供了嚴格意義上的所有權,包括:
unique_ptr 可以實現如下功能:
#include#include#includeusing namespace std;class Test{public: Test(string s) { str = s; cout << "Test creat/n"; } ~Test() { cout << "Test delete:" << str << endl; } string& getStr() { return str; } void setStr(string s) { str = s; } void print() { cout << str << endl; }private: string str;};unique_ptr fun(){ return unique_ptr(new Test("789"));}int main(){ unique_ptr ptest(new Test("123")); unique_ptr ptest2(new Test("456")); ptest->print(); ptest2 = std::move(ptest);//不能直接ptest2 = ptest if (ptest == NULL) cout << "ptest = NULL/n"; Test* p = ptest2.release(); p->print(); ptest.reset(p); ptest->print(); ptest2 = fun(); //這里可以用=,因為使用了移動構造函數 ptest2->print(); return 0;}
unique_ptr 和 auto_ptr用法很相似,不過不能使用兩個智能指針賦值操作,應該使用std::move; 而且它可以直接用if(ptest == NULL)來判斷是否空指針;release、get、reset等用法也和auto_ptr一致,使用函數的返回值賦值時,可以直接使用=, 這里使用c++11 的移動語義特性。另外注意的是當把它當做參數傳遞給函數時(使用值傳遞,應用傳遞時不用這樣),傳實參時也要使用std::move,比如foo(std::move(ptest))。它還增加了一個成員函數swap用于交換兩個智能指針的值。
std::shared_ptr 是一種智能指針,它能夠記錄多少個 shared_ptr 共同指向一個對象,從而消除 顯式的調用 delete,當引用計數變為零的時候就會將對象自動刪除。
但還不夠,因為使用 std::shared_ptr 仍然需要使用 new 來調用,這使得代碼出現了某種程度上的 不對稱。
std::make_shared 就能夠用來消除顯式的使用 new,所以 std::make_shared 會分配創建傳入參 數中的對象,并返回這個對象類型的 std::shared_ptr 指針。例如:
#include #include void foo(std::shared_ptr i) { (*i)++;}int main() { // auto pointer = new int(10); // illegal, no direct assignment // Constructed a std::shared_ptr auto pointer = std::make_shared(10); foo(pointer); std::cout << *pointer << std::endl; // 11 // The shared_ptr will be destructed before leaving the scope return 0;}
std::shared_ptr 可以通過 get() 方法來獲取原始指針,通過 reset() 來減少一個引用計數,并 通過 use_count() 來查看一個對象的引用計數。例如:
auto pointer = std::make_shared(10);auto pointer2 = pointer; // 引用計數 +1auto pointer3 = pointer; // 引用計數 +1int *p = pointer.get(); // 這樣不會增加引用計數std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3pointer2.reset();std::cout << "reset pointer2:" << std::endl;std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2 已 resetstd::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2pointer3.reset();std::cout << "reset pointer3:" << std::endl;std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3 已 reset
use_count 返回引用計數的個數
unique 返回是否是獨占所有權( use_count 為 1)
swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)
reset 放棄內部對象的所有權或擁有對象的變更, 會引起原有對象的引用計數的減少
get 返回內部對象(指針), 由于已經重載了()方法, 因此和直接使用對象是一樣的.
share_ptr雖然已經很好用了,但是有一點share_ptr智能指針還是有內存泄露的情況,當兩個對象相互使用一個shared_ptr成員變量指向對方,會造成循環引用,使引用計數失效,從而導致內存泄漏。
#include using namespace std; #include class B; class A { public: shared_ptr ptrA_B; public: A() { cout << "調用class A的默認構造函數" << endl; } ~A() { cout << "調用class A的析構函數" << endl; } }; class B { public: shared_ptr ptrB_A; public: B() { cout << "調用class B的默認構造函數" << endl; } ~B() { cout << "調用class B的析構函數" << endl; } }; int main() { shared_ptr ptrB = make_shared(); shared_ptr ptrA = make_shared(); ptrA->ptrA_B = ptrB; ptrB->ptrB_A = ptrA; }
運行結果是 A, B 都不會被銷毀,這是因為 a,b 內部的 pointer 同時又引用了 a,b,這使得 a,b 的引 用計數均變為了 2,而離開作用域時,a,b 智能指針被析構,卻只能造成這塊區域的引用計數減一,這樣 就導致了 a,b 對象指向的內存區域引用計數不為零,而外部已經沒有辦法找到這塊區域了,也就造成了 內存泄露,如圖:
weak_ptr詳解:https://blog.csdn.net/weixin_45590473/article/details/113057545
解決shared_ptr的問題的辦法就是使用弱引用指針 std::weak_ptr,std::weak_ptr 是一種弱引用(相比較 而言 std::shared_ptr 就是一種強引用)。弱引用不會引起引用計數增加,由此我們引入了弱指針。
當換用弱引用時候,最終的 釋放流程如圖:
在上圖中,最后一步只剩下 B,而 B 并沒有任何智能指針引用它,因此這塊內存資源也會被釋放。 std::weak_ptr 沒有 * 運算符和 -> 運算符,所以不能夠對資源進行操作,它的唯一作用就是用于 檢查 std::shared_ptr 是否存在,其 expired() 方法能在資源未被釋放時,會返回 false,否則返回 true。
智能指針這種技術并不新奇,在很多語言中都是一種常見的技術,現代 C++ 將這項技術引進,在 一定程度上消除了 new/delete 的濫用,是一種更加成熟的編程范式。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/119399.html
摘要:但是服務通常由服務提供者來管理的。小結通過上述的例子,基本上可以理解服務容器和服務提供者的使用。懂得了服務容器和服務提供者,理解門面也就不難了。 自動依賴注入 什么是依賴注入,用大白話將通過類型提示的方式向函數傳遞參數。 實例 1 首先,定義一個類: /routes/web.php class Bar {} 假如我們在其他地方要使用到 Bar 提供的功能(服務),怎么辦,直接傳入參數即...
摘要:成本函數成本對于線性回歸,成本函數是表示每個預測值與其預期結果之間的聚合差異的某些函數對于邏輯回歸,是計算每次預測的正確或錯誤的某些函數。成本函數的變換涉及到預測結果和實際結果之間數值距離的任何函數都不能作為成本函數。 矩陣和多特征線性回歸快速回顧之前文章的前提是:給定特征——任何房屋面積(sqm),我們需要預測結果,也就是對應房價($)。為了做到這一點,我們:我們找到一條「最擬合」所有數據...
摘要:上面這三種均不造成重載,現在來說明原因。結論對于引用返回,返回的對象必須是棧幀銷毀后還存在的。全局,靜態,未銷毀的函數棧幀當中的都是可以的指針與引用如圖兩者底層實現差不多,引用是用指針模擬的。不建議聲明和定義分離,分離會導致鏈接錯誤。 ...
摘要:大學,光學工程研究生畢業,和程序猿完全不搭邊。那怎么辦,試著學一學唄,學習才是程序猿的天性。所以我在想程序猿是不是都需要新知識刺激一下,才能保持興奮的頭腦。有句話說的很對程序猿就像好奇的貓,追著毛球的線頭玩,最后一個毛球在腦袋里攪漿糊。 說說我自己的經歷。211大學,光學工程研究生畢業,和程序猿完全不搭邊。 畢業后進了成都某國字頭研究所,在行業里摸爬滾打了四年,2018年機緣巧合在家養...
摘要:寫的人越來越想,閱讀的人越來越多的這個信息冗余的年代,會寫就代表會思考轉載保留程序員為什么值得寫博客為什么要寫博文寫一篇博文意味著要花一定的時間,有時候可能是一個小時,有時候可能會更多,于是人們開始去。 Hire Great Writers 仿佛這是寫給自己看的,不過這在其中也有著相當有趣的意義 。雖然自己算是一個能寫的人,或許這算是一種不算才華的才華,寫博文的意義通常不會在于去描述...
閱讀 1330·2021-11-25 09:43
閱讀 738·2021-11-18 10:02
閱讀 2862·2021-09-07 09:59
閱讀 2747·2021-08-30 09:44
閱讀 2920·2019-08-30 13:17
閱讀 2305·2019-08-29 12:17
閱讀 1673·2019-08-28 17:57
閱讀 1281·2019-08-26 14:04