一、智能指針ios
二、關於string對象、string指針和空指針的總結算法
四、標準模板庫ide
01)在使用new爲指針分配內存空間的時候,有可能會出現忘記添加delete或者是沒有忘記但不執行delelte的狀況 ,此時就會致使內存泄露,例如以下狀況:函數
1 void remodel(std::string & str) 2 { 3 std::string * ps = new std::string(str); 4 double * pd1 = new double[8]; //new返回一個能夠存儲8個double行數據的地址,pd1是一個指針 5 ... 6 if(weird_thing()) 7 throw exception(); //若是執行此句,那麼就有可能不執行下面的delete 8 str = *ps; 9 delete ps; 10 return; 11 }
02)使用智能指針
(1)這三個智能指針模板(auto_ptr、unique_ptr和shared_ptr)都定義了相似指針的對象,能夠將new得到的地址賦給這些對象。當智能指針過時時,其析構函數將使用delete
來釋放內存。下圖說明了常規指針和auto_ptr之間的差異.(unique_ptr和shared_ptr的行爲和auto_ptr相似)
(2)智能指針使用語法(須要包含頭文件memory):spa
1 auto_ptr<double> pd(new double); //相似於double* pd = new doouble; 只不過auto_ptr不用使用delete釋放內存 2 auto_ptr<string> ps(new string); //相似於string* ps = new string; ps後面的new string是new返回的指針,指向新分配的內存塊,做爲構造函數auto_ptr<double>的參數
其餘兩種智能指針也是一樣的語法:.net
1 unique_ptr<double> pd(new double); 2 shared_ptr<string> ps(new string);
使用智能指針修改remodel()函數後的結果:指針
1 #include <memory> 2 void remodel(std::string & str) 3 { 4 std::auto_ptr<std::string> ps(new std::string(str)); //將string對象str的地址賦給pd 5 ... 6 if(weird_thing()) 7 throw exception(); //若是執行此句,那麼就有可能不執行下面的delete 8 str = *ps; 9 return; 10 }
03)使用智能指針完整的一個例子:code
使用智能指針指向一個類對象,其中類對象的參數爲初始化該對象時候傳入類構造函數的參數(對象
指針都是要指向一個對象的吧,那麼對象在建立的時候確定是包含參數的了啊,因此指針指向的內存區域也是有參數的)。
1 #include <iostream> 2 #include <string> 3 #include <memory> 4 5 class Report 6 { 7 private: 8 std::string str; 9 public: 10 Report(const std::string s) : str(s) 11 { 12 std::cout << "Object created!\n"; 13 } 14 ~Report() 15 { 16 std::cout << "Object deleted!\n"; 17 } 18 void show() const 19 { 20 std::cout << str << "\n"; //打印建立Report對象時設置的私有數據 21 } 22 }; 23 24 int main() 25 { 26 /*使用代碼塊,這樣在執行完代碼塊以後,在代碼塊中建立的全部變量都將被銷燬*/ 27 { 28 std::auto_ptr<Report> ps(new Report("using auto_ptr")); //等價於Report* ps = new Report("using auto_ptr"); 29 ps->show(); //Report指針Report類中的方法 30 }//執行到這裏後ps將會自動調用auto_ptr類的析構函數,將ps銷燬並釋放內存 31 /*同理使用代碼塊*/ 32 { 33 std::shared_ptr<Report> pd(new Report("using shared_ptr")); //ps指向數據爲"using shared_ptr"的Report對象 34 pd->show(); 35 }//執行到這裏後pd將會自動調用auto_ptr類的析構函數,將ps銷燬並釋放內存 36 /*同理使用代碼塊*/ 37 { 38 std::unique_ptr<Report> pa(new Report("using unique_ptr")); //ps指向數據爲"using unique_ptr"的Report對象 39 pa->show(); 40 } 41 system("pause"); 42 return 0; 43 }
執行結果:
注意:
1 std::shared_ptr<double> pd; //建立智能指針,但不分配內存 2 double* p_reg = new double; //建立普通指針p_reg,並分配內存 3 //pd = p_reg; //將普通指針賦給智能指針不合法!! 4 pd = std::shared_ptr<double>(p_reg); //容許,使用了顯式轉換 5 //std::shared_ptr<double> pshared = p_reg; //不容許,由於進行了隱式轉換,而shared_ptr含一個參數的構造函數使用了關鍵字explicit 6 std::shared_ptr<double> pshared(p_reg); //容許
04)須要注意的問題
對於三種智能指針都應該避免的是:
1 std::string vacation("I wandered longly as a cloud."); //因爲不是使用new建立的字符變量vacation,因此vacation是非堆變量 2 std::shared_ptr<string> pvac(&vacation); //當pvac過時時,就會調用delete刪除pvac指向的非堆內存,這是不容許的!!!
std::string str00 = "能夠了---陳奕迅"; std::cout << str00 << std::endl; //這樣是能夠正常打印的,打印字符串能夠了---陳奕迅 std::string* str0 = new std::string("能夠了---陳奕迅"); std::cout << str0 << std::endl; //這樣是能夠正常打印的,打印str0的地址(由於str0自己就是一個指針) std::string* str1 = new std::string("能夠了---陳奕迅"); std::cout << *str1 << std::endl; //這樣是能夠正常打印的,打印字符串能夠了---陳奕迅 std::string* str2; //空指針 std::cout << *str2 << std::endl; //打印空指針就會報錯了,至關於下面的films[2]放棄原來對指針的控制權
01)auto_ptr建立的智能指針擁有全部權的概念,賦值後將丟失該全部權。編譯器會認爲下面的第三句是錯誤的(書上寫的是不會標紅,但實際上標紅了,估計是沒有將下面三句放入主函數中的事情)
1 auto_ptr<string> ps(new string("YiYA"); //建立智能指針ps,並使ps指向字符串YiYA 2 auto_ptr<string> pd; //建立智能指針,有內存可是沒有指向字符串 3 pd = ps; //對於auto_ptr來講,該句會致使ps成爲空指針;即auto_ptr建立的智能指針擁有全部權的概念,賦值後將丟失該全部權
02)unique_ptr建立的智能指針也擁有全部權的概念,只不過unique_prt策略更嚴格(書上寫的是:假如將上面的auto_ptr替換爲unique_ptr,那麼第三句是錯誤的,將會被標紅)
03)對於shared_ptr
shared_ptr建立智能更高的指針,跟蹤引用特定對象的智能指針數.這成爲引用計數。例如智能指針賦值時,計數將加1,
而指向同一字符串的指針過時時,計數將減1.僅當最後一個指向同一字符串的指針過時時,才調用delete
04)對於使用auto_ptr,第三句會標紅,既有好處也有壞處:
好處:pd將接管ps的對"YiYA"的全部權,ps對該string對象的全部權將被剝奪,這是件好事,可防止ps和pd的析構函數同時刪除同一個對象;
壞處:若是隨後程序將使用ps,這將是一件壞事,由於ps再也不指向有效的數據。
05)對於02unique_ptr智能指針不能相互賦值的反例:unique_ptr智能指針做爲函數返回值,賦值給一個unique_ptr智能指針是容許的,以下:
1 #include <iostream> 2 #include <string> 3 #include <memory> 4 5 using namespace std; 6 7 //假設有子函數 8 unique_ptr<string> demo(const char* s) //返回值爲指向string的智能指針 9 { 10 unique_ptr<string> temp(new string(s)); 11 return temp; //在demo()被執行完後,返回的temp很快被銷燬,沒有機會用它來訪問無效數據 12 } 13 14 //調用程序爲 15 int main() 16 { 17 unique_ptr<string> ps; 18 ps = demo("Uniquely special"); //這樣使用兩個unique_ptr指針之間相互賦值時容許的; 19 //在demo()被執行完後,返回的temp很快被銷燬,沒有機會用它來訪問無效數據 20 cout << *ps << endl; //打印Uniquely special 21 22 cin.get(); 23 return 0; 24 }
總之,程序試圖將一個unique_ptr賦給另外一個時,若是源unique_ptr是一個臨時右值,編譯器容許這樣作;若是源unique_ptr將存在很長一段時間,編譯器將禁止這樣作。編譯器將容許這樣作:
1 unique_ptr<string> pu1(new string("Hi ho!")); 2 unique_ptr<string> pu2; 3 pu2 = pu1; //不容許,由於pu1將存在很長一段時間 4 unique_ptr<string> pu3; 5 pu3 = unique_ptr<string>(new string("Yo!")); //容許!string("Yo!")將返回一個臨時string對象,以後將很快被銷燬
可是若是確實想使用上面代碼中的第三句,可使用std::move(),即將上面的第三句改成:
ps2 = std::move(ps1);
下面程序中第十八行出現了智能指針相互賦值從而致使films[2]成爲空指針,打印空指針進而會報錯
1 #include <iostream> 2 #include <string> 3 #include <memory> 4 5 int main() 6 { 7 using namespace std; 8 /*建立一個數組films,數組內包含五個指針*/ 9 auto_ptr<string> films[5] = 10 { 11 auto_ptr<string> (new string("Fowl Balls")), //能夠沒有變量的名字;注意最後一個是逗號而不是分號 12 auto_ptr<string> (new string("Duck Walks")), 13 auto_ptr<string> (new string("Chicken Runs")), 14 auto_ptr<string> (new string("Turkey Errors")), 15 auto_ptr<string> (new string("Goose Eggs")) 16 }; 17 auto_ptr<string> pwin; //建立一個指向string對象的智能指針對象 18 //pwin = films[2]; //這一句將致使films[2]再也不引用該字符串,從而fimls[2]變成了一個空指針,在打印*films[2]的是時候程序會出現意外 19 cout << "The nominess for best avin baseball film are:\n"; 20 for (int i = 0; i < 5; i++) 21 cout << *films[i] << endl; 22 //cout << "The winner is " << *pwin << "!\n"; 23 cin.get();//輸入任意字符後終止程序 24 return 0; 25 }
可是將以上的全部auto_ptr替換爲shared_ptr,上述程序將不會報錯,正常執行。
06)智能指針的選擇
(1)若是程序要使用多個指向同一對象的之恩,應選擇shared_ptr。這樣的狀況包括:有一個指針數組,並使用一些輔助指針來標記特定的元素;兩個對象包含都指向第三個對象的指針;STL容器包含指針。
(2)若是程序不須要多個指向同一個對象的指針,則課使用unique_ptr。可將unique_ptr存儲到StL容器中,只要不調用將一個unique_ptr複製或賦給另外一個的方法或算法
1 #include <iostream> 2 #include <string> 3 #include <memory> 4 #include <vector> 5 #include <algorithm> //for for_each() 6 7 using namespace std; 8 9 /*建立返回值爲unique_ptr智能指針的子函數make_int()*/ 10 unique_ptr<int> make_int(int n) 11 { 12 return unique_ptr<int>(new int(n)); 13 } 14 15 /*顯示unique_ptr指針指向的內容的子函數*/ 16 void show(unique_ptr<int> & pi) 17 { 18 cout << *pi << endl; 19 } 20 21 int main() 22 { 23 //vector<typeName> vt(n_elem);//建立一個vector對象vt,能夠存儲n_elem個類型爲typeName的元素 24 vector<unique_ptr<int>> vp(8); //建立能夠存儲8個指向int型數據智能指針的vector對象vp 25 for (int i = 0; i < vp.size(); i++) 26 vp[i] = make_int(rand() % 1000); //使用子函數make_int()對vector對象vp填充數據 27 vp.push_back(make_int(rand() % 1000)); //將make_int()子函數返回的unique_ptr智能指針填充到vector對象vp的尾部 28 for_each(vp.begin(), vp.end(), show); 29 }
對於for_each()函數:
1 for_each(_InputIterator __first, _InputIterator __last, _Function __f) 2 /** 3 * @brief Apply a function to every element of a sequence. 4 * @ingroup non_mutating_algorithms 5 * @param __first An input iterator. 循環開始迭代器 6 * @param __last An input iterator. 循環結尾迭代器 7 * @param __f A unary function object. 處理函數 8 * @return @p __f (std::move(@p __f) in C++0x). 9 * 10 * Applies the function object @p __f to each element in the range 11 * @p [first,last). @p __f must not modify the order of the sequence. 12 * If @p __f has a return value it is ignored. 13 */
for_each()參考博客:https://blog.csdn.net/raozhufa/article/details/99741195
執行結果:
07)能夠將unique_ptr指針賦值給shared_ptr,可是反過來是不能夠的,如:
1 unique_ptr<int> pup(make_int(rand() % 1000)); //make_int()返回的unique_int指針做爲參數 2 shared_ptr<int> spp(pup); //不容許,雖然也是將unique_ptr指針賦值給shared_ptr 3 shared_ptr<int> spr(make_int(rand() % 1000)); //容許
01)STL提供了一組表示容器、迭代器、函數對象和算法的模板。容器時一個與數組相似的單元,能夠存儲若干個類型相同的值;
迭代器可以用來遍歷容器,與可以遍歷數組的指針相似,是廣義指針;函數對象時相似於函數的對象,能夠是類對象或函數指針,
其中函數指針包括函數名,由於函數名被用做指針。
02)如下代碼爲建立vector容器對象:
1 #include <vector> 2 using std::vector; 3 vector<int> ratings(5); //建立一個包含5個int型數據的vector對象ratings,注意這裏使用的是小括號!!而vector<int> ratings[5]是建立五個vector數組(或建立5個vector對象ratings[1]表示第一個vector對象) 4 for(int i=0;i<5;i++) 5 { 6 std::cout << ratings[i] << std::endl; //運算符[]被重載,可使用一般的數組表示方法來訪問各個元素 7 }
01)迭代器:它是一個廣義指針。事實上,它能夠是指針,也能夠是一個可對其執行相似指針的操做(如接觸引用和遞增)的對象。
要爲vector的double類型聲明一個迭代器,能夠這樣作:
1 vector<double>::iterator pd; //pd是一個迭代器,pd指向類型爲double的vector容器
假設scores是一個vector<double>對象:
1 vector<double> scores;
則可使用迭代器pd執行以下操做:
1 pd = scores.begin(); //使pd指向scores中的第一個元素 2 *pd = 23.3; //對pd解除引用,並對scores中的第一個元素賦值; 3 ++pd; //使pd指向scores中的下一個元素
02)auto替代vector<double>::iterator
能夠這樣作:
1 vector<double>::iterator pd = scores.begin();
也能夠這樣作:
1 auto pd = scores.begin();
03)end()函數
該函數返回一個超越結尾的迭代器。超越結尾的迭代器,是指向容器最後一個元素後面那個元素的迭代器。與c風格字符串最後一個字符後面的
相似,只是空字符是一個值,而「超越結尾的迭代器」是一個指向元素的指針(迭代器)。所以遍歷scores容器中的元素可使用以下方法:
1 vector<double>::iterator pd; 2 vector<double> scores;
1 for(pd = scores.begin(); pd != scores.end(); pd++) 2 cout << *pd << endl;
04)push_back()函數
該函數將元素添加到矢量(容器)末尾。無需瞭解元素的數目,只要可以取得足夠的內存,程序就能夠根據須要增長容器的長度,如:
1 vector<double> scores; 2 double temp; 3 while(cin>>temp && temp >= 0) 4 scores.push_back(temp); //將temp添加到scores容器尾 5 cout << "You entered " << scores.size() << " scores.\n";
05)erase()
該函數刪除矢量(容器)指定區間的元素。它接受兩個迭代器參數,這些參數定義了要刪除的區間。例如:
1 scores.erase(scores.begin(), scores.begin()+2); //刪除scores中第一個和第二個元素
06)inser()
該方法接受三個迭代器參數,第一個參數指定了新元素的插入位置,第二個和第三個定義了被插入的區間,該區間一般是另外一個容器對象的一部分
1 vector<double> old_v; //建立一個指向double的vector對象old_v 2 vector<double> new_v; //建立一個指向double的vector對象new_v 3 old_v.inset(old_v.begin(), new_v.begin()+1, new_v.end()); //將new_v中除第一個元素外的全部元素插入到old_v中,插入第一個位置爲old_v第一個位置
07)for_each()
該函數接受三個參數。前兩個是定義容器中(要顯示或者是其餘操做的)區間的迭代器,最後是一個指向函數的指針,
該函數指出了要對這個區間的數據作的操做,以下示例:
能夠將代碼:
1 truct Review{ 2 std::string title; 3 int rating; 4 }; //定義一個結構體 5 vector<Review> books; //建立一個指向Review的vector對象books 6 vector<Review>::iterator pr; //pr是一個迭代器,pr指向類型爲double的vector容器 7 for(pr = books.begin(); pr != books.end(); pr++) 8 ShowReview(*pr); //顯式books容器中的內容
替換爲:
1 struct Review{ 2 std::string title; 3 int rating; 4 }; //定義一個結構體 5 vector<Review> books; //建立一個指向Review的vector對象books 6 vector<Review>::iterator pr; //pr是一個迭代器,pr指向類型爲double的vector容器 7 for_each(books.begin(),books.end(),ShowReview);
08)基於範圍的for循環
(01)第五章的示例:
1 double prices[5] = {4.99,2.32,5.65,4.56,9.68}; 2 cout << x <<endl;
(02)第十六章的示例:
1 for_each(books.begin(), books.end(), ShowReivew); //其中books爲vector對象,ShowReview爲一個子函數 2 //可替換爲: 3 for(auto x : books) //根據books的類型(vector<Review>),編譯器會判斷出x的類型爲Review 4 ShowReview(x);
可是基於範圍的for循環能夠修改容器中的內容,訣竅是使用一個引用參數,以下所示:
1 void InflatReview(Review & r) //其中Review爲一個結構,包括int型數據rating 2 { 3 r.rating++; 4 } 5 for(auto & x : books) 6 InflatReview(x);
09)關於vector對象
1 vector<int> ratings(5); //建立一個包含5個int型數據的vector對象ratings,注意這裏使用的是小括號!! 2 vector<int> ratings[5] //是建立五個vector數組(或建立5個vector對象ratings[1]表示第一個vector對象) 3 vector<int> a[10]; //a是一個向量數組 4 vector<int> b(10); //b是一個向量 5 vector<vector<int>> c; //c是一個響亮 6 vector<vector<int>,10> d; //d是一個向量數組