C++_十六章_智能指針_關於string對象、string指針和空指針的總結_關於智能指針相互賦值會致使出現空字符的問題_標準模板庫

目錄

一、智能指針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 }
delete有可能不會被執行的狀況

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 }
修改remote()後的結果

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指向的非堆內存,這是不容許的!!!

 二、關於string對象、string指針和string空指針的總結

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 }
View Code

總之,程序試圖將一個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對象,以後將很快被銷燬
View Code

可是若是確實想使用上面代碼中的第三句,可使用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 }
View Code

對於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是一個向量數組
相關文章
相關標籤/搜索