C++記憶體管理之shared_ptr
參考連結:C++記憶體管理之shared_ptr - 小念之歌 - 部落格園 (cnblogs.com)
1.初始化sahred_ptr
智慧指標的使用方式與普通指標類似。解引用一個智慧指標返回它指向的物件。如果在一個條件判斷中使用智慧指標,效果就是檢測它是否為空:
#include <iostream> using namespace std; int main() { /*---------空指標------------*/ shared_ptr<string> p1; if(!p1) //!預設初始化的智慧指標中儲存著一個空指標!並不是""空字串cout<<"p1==NULL"<<endl; /*---------初始化------------*/ shared_ptr<string> p2(new string); if(p2&&p2->empty()){ //!需要注意的時empty時屬於string的成員函式。 *p2="helloworld"; cout<<*p2<<endl; } // shared_ptr<int> pa = new int(1);//!error:不允許以暴露裸漏的指標進行賦值操作。//一般的初始化方式 shared_ptr<string> pint(new string("normal usage!")); cout<<*pint<<endl; //推薦的安全的初始化方式 shared_ptr<string> pint1 = make_shared<string>("safe uage!"); cout<<*pint1<<endl; }
2.關於get()函式;
智慧指標定義了一個名為get的函式,它返回一個內建指標,指向智慧指標的管理的物件。此函式設定的初衷是當我們向不能使用智慧指標的程式碼傳遞一個內建指標。使用get返回指標的程式碼不能delete此指標
注意:get用來將指標的訪問許可權傳遞給程式碼,只有在確定程式碼不會delete指標的情況下,才能使用get。特別是,永遠不要用get初始化另一個智慧指標或者為另一個智慧指標賦值!
#include <iostream> #include <memory> using namespace std; void useShared_ptr(int *p) { cout<<*p<<endl; } void delePointer(int *p) { delete p; } int main(int argc, char *argv[]) { shared_ptr<int> p1 = make_shared<int>(32); // shared_ptr<int>p2(p1.get()); //!錯誤的用法:但是p1、p2各自保留了對一段記憶體的引用計數,其中有一個引用計數耗盡,資源也就釋放了。 useShared_ptr(p1.get()); // delePointer(p1.get()); //!error: return 0; }
3.關於mak_shared函式:
最安全的分配和使用動態記憶體的方法是呼叫一個名為make_shared的標準庫函式,此函式在動態記憶體中分配一個物件並初始化它,返回此物件的shared_ptr。與只能指標一樣,make_shared也定義在標頭檔案memory中。
#include <iostream> using namespace std; int main() { shared_ptr<int> p3 = make_shared<int>(42); cout<<*p3<<endl; shared_ptr<string> pstr = make_shared<string>("99999"); cout<<*pstr<<endl; shared_ptr<int> pint = make_shared<int>(); //!預設初始化為 0 cout<<*pint<<endl; auto pau = make_shared<string>("auto"); //!更簡單,更常用的方式。 cout<<*pau<<endl; }
4.shared_ptr物件的銷燬
1)管理動態陣列
預設情況下,shared_ptr指向的動態的記憶體是使用delete來刪除的。這和我們手動去呼叫delete然後呼叫物件內部的解構函式是一樣的。與unique_ptr不同,shared_ptr不直接管理動態陣列。如果希望使用shared_ptr管理一個動態陣列,必須提供自定義的刪除器來替代delete 。
#include <iostream>using namespace std; class DelTest { public: DelTest(){ j= 0; cout<<" DelTest()"<<":"<<i++<<endl; } ~DelTest(){ i = 0; cout<<"~ DelTest()"<<":"<<i++<<endl; } static int i,j; };int DelTest::i = 0;int DelTest::j = 0; void noDefine() { cout<<"no_define start running!"<<endl; shared_ptr<DelTest> p(new DelTest[10]); } void slefDefine() { cout<<"slefDefine start running!"<<endl; shared_ptr<DelTest> p(new DelTest[10],[](DelTest *p){delete[] p;}); } //!傳入lambada表示式代替delete操作。 int main() { noDefine(); //!構造10次,析構1次。記憶體洩漏。 cout<<"----------------------"<<endl; slefDefine(); //!構造次數==析構次數 無記憶體洩漏 }
2)管理非常規動態物件
某些情況下,有些動態記憶體也不是我們new出來的,如果要用shared_ptr管理這種動態記憶體,也要自定義刪除器。
#include <iostream>#include <stdio.h> #include <memory> using namespace std; void closePf(FILE * pf) { cout<<"----close pf after works!----"<<endl; fclose(pf); } int main() { // FILE * fp2 = fopen("bin2.txt", "w"); // if(!pf) // return -1; // char *buf = "abcdefg"; // fwrite(buf, 8, 1, fp2); // fclose(fp2); shared_ptr<FILE> pf(fopen("bin2.txt", "w"),closePf); cout<<"*****start working****"<<endl; if(!pf) return -1; char *buf = "abcdefg"; fwrite(buf, 8, 1, pf.get()); //!確保fwrite不會刪除指標的情況下,可以將shared_ptr內建指標取出來。 cout<<"----write int file!-----"<<endl; } //!即可以避免異常發生後無法釋放記憶體的問題,也避免了很多人忘記執行fclose的問題。
在這裡可以設想一下TCP/IP中連結的開啟和關閉的情況,同理都可以使用智慧指標來管理。
總結:
最後總結一下上面所陳述的內容,也是shared_ptr使用的基本規範
1)不使用相同的內建指標值初始化(或reset)多個智慧指標。
2)不delete get函式返回的指標。
3)如果你使用了get返回的指標,記住當最後一個對應的智慧指標銷燬後,你的指標就變為無效了。
4)如果你使用智慧指標管理的資源不是new分配的記憶體,記得傳遞給他一個刪除器。
weak_ptr
weakptr使用的比較少,如有興趣瞭解,請去參考該篇文章:https://www.cnblogs.com/DswCnblog/p/5628314.html