<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-204  評論-149  文章-0  trackbacks-0
    Effective C++
    條款31: 千萬不要返回局部對象的引用,也不要返回函數內部用new初始化的指針的引用


     1 #include<iostream>
     2 #include<string>
     3 
     4 using namespace std;
     5 
     6 class SimpleCat
     7 {
     8 public:
     9     SimpleCat(void);
    10     SimpleCat(int age,int weight);
    11     int GetAge(){return itsAge;}
    12     int GetWeight(){return itsWeight;}
    13 
    14 private:
    15     int itsAge;
    16     int itsWeight;
    17     //int itsAge=5;//1>.\SimpleCat.cpp(14) : error C2864: “SimpleCat::itsAge”: 只有靜態常量整型數據成員才可以在類中初始化
    18 
    19 public:
    20     virtual ~SimpleCat(void);
    21 };
    22 
    23 SimpleCat::SimpleCat(void)
    24 {
    25     cout<<"SimpleCat constructor "<<this<<endl;
    26 }
    27 
    28 SimpleCat::SimpleCat(int age,int weight)
    29 {
    30     cout<<"SimpleCat constructor "<<this<<endl;
    31     itsAge = age;
    32     itsWeight = weight;
    33 }
    34 
    35 SimpleCat::~SimpleCat(void)
    36 {
    37     cout<<"SimpleCat destructor"<<this<<endl;
    38 }
    39 
    40 SimpleCat &TheFunction();
    41 
    42 int main()
    43 {
    44     SimpleCat myCat;
    45     cout<<myCat.GetAge()<<endl;//這個值區別于java不是賦初值為0的,而是一個隨機的值
    46 //    cout<<myCat.itsAge<<endl;
    47 
    48     cout<<"------------------------"<<endl;
    49     SimpleCat &rCat = TheFunction();
    50     int age = rCat.GetAge();
    51     cout<<"rCat is "<<age<<"yeas old!"<<endl;
    52     cout<<"&rCat:  "<<&rCat<<endl;
    53     SimpleCat *pCat = &rCat;
    54     //delete rCat;//不能對引用使用delete
    55     delete pCat;
    56     //delete好像沒有釋放內存,怎么獲取的還是原來的值
    57     //可能在這個內存區域存放的還是原來的?先new string后再調用也沒變,與編譯器有關還是什么??
    58     for(int i =0;i<10;i++)
    59     {
    60         //想通過創建string對象來填充之前的內存區間,好像沒用
    61         string *= new string("abcdefghijklmn");
    62     }
    63 
    64     //這時問題來了,rCat.getAge()為123了
    65     SimpleCat *pSecond = new SimpleCat(123,444);
    66     
    67     cout<<"delete pCat后再使用rCat引用會發生什么問題???"<<endl;
    68     cout<<"delete pCat后 &rCat"<<&rCat<<endl;
    69     cout<<"delete pCat后 rCat.age"<<rCat.GetAge()<<endl;
    70 
    71     cout<<"--------------------------"<<endl;
    72     SimpleCat myCat2 = TheFunction();//這個會發生內存泄漏,在函數中申請的內存會得不到釋放
    73                                      //myCat2是通過默認的拷貝函數來進行初始化的
    74     cout<<"myCat2的地址是 "<<&myCat2<<endl;
    75     return 0;
    76 }
    77 
    78 
    79 //這個函數在返回時,是否會創建一個臨時引用變量???
    80 //TheFunctionTwo(SimpleCat &simpleCat)這個函數在傳遞參數時是否會創建一個臨時的引用變量
    81 //臨時的引用變量的作用域范圍是什么
    82 SimpleCat &TheFunction()
    83 {
    84     SimpleCat *pFrisky = new SimpleCat(5,9);
    85     cout<<"pFrisky: "<<pFrisky<<endl;
    86     return *pFrisky;
    87 }
    88 
    89 
    90 

    函數TheFunction()的返回值被賦值給對SimpleCat類的一個引用,然后使用這個引用來獲得對象的成員變量age的值
    為了證明函數TheFunction()在自由存儲區中創建的對象被賦給了函數main()聲明的引用,對rCat采取取址運算,很明顯,它顯示了其引用的對象的地址,并且和自由存儲區中的對象的地址相匹配。
    這一切看上去都很正確,但是如何釋放被占用的內存,對于引用是不能使用delete運算符的,一個筆記聰明的方法是聲明另一個指針,并使用由rCat得到的地址對它進行初始化。這樣做確實可以釋放內存,避免了內存的泄漏,但是存在一個小問題,在delete pCat后,rCat引用又代表什么呢??引用必須是一個實際對象的別名;如果引用一個空對象的話(現在就是這種情況)那么程序就是無效的。。

    注意:不能過分去強調一個使用對空對象的引用的程序,也行能通過編譯,但是即使程序通過了編譯,它仍然是病態的,而且執行結果也是不可預料的

    關于這個問題,存在這3種解決方案
    第1種是在第49行聲明一個SimpleCat類對象,然后從函數TheFunction()中采取值傳遞的方式返回它的局部對象,這時不能在自由存儲區來分配對象空間,不能使用new,
    第2種是在函數TheFunction()中聲明一個位于自由存儲區中的SimpleCat類的對象,但是讓函數返回一個指向內存區域的指針,然后完成對對象的操作,調用函數可以刪除這個指針
    第3種可行的方案,也是正確的,是在調用函數中聲明對象,然后通過引用的方式把它傳遞給函數TheFunction()


    也意味在main函數中不能這樣使用了

    posted on 2009-05-11 23:01 Frank_Fang 閱讀(1138) 評論(2)  編輯  收藏 所屬分類: C++編程

    評論:
    # re: C++函數返回自由內存區間的對象的引用 2009-06-06 22:06 | yyy
    條款31: 千萬不要返回局部對象的引用,也不要返回函數內部用new初始化的指針的引用

    本條款聽起來很復雜,其實不然。它只是一個很簡單的道理,真的,相信我。

    先看第一種情況:返回一個局部對象的引用。它的問題在于,局部對象 ----- 顧名思義 ---- 僅僅是局部的。也就是說,局部對象是在被定義時創建,在離開生命空間時被銷毀的。所謂生命空間,是指它們所在的函數體。當函數返回時,程序的控制離開了這個空間,所以函數內部所有的局部對象被自動銷毀。因此,如果返回局部對象的引用,那個局部對象其實已經在函數調用者使用它之前被銷毀了。

    當想提高程序的效率而使函數的結果通過引用而不是值返回時,這個問題就會出現。下面的例子和條款23中的一樣,其目的在于詳細說明什么時候該返回引用,什么時候不該:

    class rational { // 一個有理數類
    public:
    rational(int numerator = 0, int denominator = 1);
    ~rational();

    ...

    private:
    int n, d; // 分子和分母

    // 注意operator* (不正確地)返回了一個引用
    friend const rational& operator*(const rational& lhs,
    const rational& rhs);
    };

    // operator*不正確的實現
    inline const rational& operator*(const rational& lhs,
    const rational& rhs)
    {
    rational result(lhs.n * rhs.n, lhs.d * rhs.d);
    return result;
    }

    這里,局部對象result在剛進入operator*函數體時就被創建。但是,所有的局部對象在離開它們所在的空間時都要被自動銷毀。具體到這個例子來說,result是在執行return語句后離開它所在的空間的。所以,如果這樣寫:

    rational two = 2;

    rational four = two * two; // 同operator*(two, two)


    函數調用時將發生如下事件:

    1. 局部對象result被創建。
    2. 初始化一個引用,使之成為result的另一個名字;這個引用先放在另一邊,留做operator*的返回值。
    3. 局部對象result被銷毀,它在堆棧所占的空間可被本程序其它部分或其他程序使用。
    4. 用步驟2中的引用初始化對象four。

    一切都很正常,直到第4步才產生了錯誤,借用高科技界的話來說,產生了"一個巨大的錯誤"。因為,第2步被初始化的引用在第3步結束時指向的不再是一個有效的對象,所以對象four的初始化結果完全是不可確定的。

    教訓很明顯:別返回一個局部對象的引用。

    "那好,"你可能會說,"問題不就在于要使用的對象離開它所在的空間太早嗎?我能解決。不要使用局部對象,可以用new來解決這個問題。"象下面這樣:

    // operator*的另一個不正確的實現
    inline const rational& operator*(const rational& lhs,
    const rational& rhs)
    {
    // create a new object on the heap
    rational *result =
    new rational(lhs.n * rhs.n, lhs.d * rhs.d);

    // return it
    return *result;
    }

    這個方法的確避免了上面例子中的問題,但卻引發了新的難題。大家都知道,為了在程序中避免內存泄漏,就必須確保對每個用new產生的指針調用delete,但是,這里的問題是,對于這個函數中使用的new,誰來進行對應的delete調用呢?

    顯然,operator*的調用者應該負責調用delete。真的顯然嗎?遺憾的是,即使你白紙黑字將它寫成規定,也無法解決問題。之所以做出這么悲觀的判斷,是基于兩條理由:

    第一,大家都知道,程序員這類人是很馬虎的。這不是指你馬虎或我馬虎,而是指,沒有哪個程序員不和某個有這類習性的人打交道。想讓這樣的程序員記住無論何時調用operator*后必須得到結果的指針然后調用delete,這樣的幾率有多大呢?也是說,他們必須這樣使用operator*:

    const rational& four = two * two; // 得到廢棄的指針;
    // 將它存在一個引用中
    ...

    delete &four; // 得到指針并刪除

    這樣的幾率將會小得不能再小。記住,只要有哪怕一個operator*的調用者忘了這條規則,就會造成內存泄漏。

    返回廢棄的指針還有另外一個更嚴重的問題,即使是最盡責的程序員也難以避免。因為常常有這種情況,operator*的結果只是臨時用于中間值,它的存在只是為了計算一個更大的表達式。例如:

    rational one(1), two(2), three(3), four(4);
    rational product;

    product = one * two * three * four;

    product的計算表達式需要三個單獨的operator*調用,以相應的函數形式重寫這個表達式會看得更清楚:

    product = operator*(operator*(operator*(one, two), three), four);

    是的,每個operator*調用所返回的對象都要被刪除,但在這里無法調用delete,因為沒有哪個返回對象被保存下來。

    解決這一難題的唯一方案是叫用戶這樣寫代碼:

    const rational& temp1 = one * two;
    const rational& temp2 = temp1 * three;
    const rational& temp3 = temp2 * four;

    delete &temp1;
    delete &temp2;
    delete &temp3;

    果真如此的話,你所能期待的最好結果是人們將不再理睬你。更現實一點,你將會在指責聲中度日,或者可能會被判處10年苦力去寫威化餅干機或烤面包機的微代碼。

    所以要記住你的教訓:寫一個返回廢棄指針的函數無異于坐等內存泄漏的來臨。

    另外,假如你認為自己想出了什么辦法可以避免"返回局部對象的引用"所帶來的不確定行為,以及"返回堆(heap)上分配的對象的引用"所帶來的內存泄漏,那么,請轉到條款23,看看為什么返回局部靜態(static)對象的引用也會工作不正常。看了之后,也許會幫助你避免頭痛醫腳所帶來的麻煩。
      回復  更多評論
      
    # re: C++函數返回自由內存區間的對象的引用 2009-06-06 22:24 | yyy
    const rational& four = two * two; // 得到廢棄的指針;
    // 將它存在一個引用中

    還必須得使用引用,否則會發生內存泄漏
      回復  更多評論
      
    主站蜘蛛池模板: 日韩吃奶摸下AA片免费观看| 99免费观看视频| 国产黄色片在线免费观看| 亚洲人成图片网站| 最刺激黄a大片免费网站| 亚洲丁香色婷婷综合欲色啪| 久久这里只精品99re免费| 97久久精品亚洲中文字幕无码| 一区二区免费视频| 亚洲国产精品一区二区久| 啦啦啦高清视频在线观看免费 | 中文字幕亚洲免费无线观看日本 | 亚洲国产精品va在线播放 | 99久久精品毛片免费播放| 亚洲精品乱码久久久久久自慰| 四虎影视无码永久免费| 亚洲AV无码乱码国产麻豆穿越| 67pao强力打造国产免费| 亚洲欧洲精品在线| 全免费a级毛片免费**视频| 美女视频免费看一区二区| 在线亚洲精品福利网址导航| 精品一卡2卡三卡4卡免费视频| 久久久久亚洲AV成人片| 妞干网手机免费视频| 有码人妻在线免费看片| 久久久久亚洲av无码专区蜜芽| 一个人免费高清在线观看| 国产精品亚洲一区二区三区| 日本红怡院亚洲红怡院最新| 成人免费毛片内射美女-百度| 美女被暴羞羞免费视频| 久久久综合亚洲色一区二区三区| 成人看的午夜免费毛片| 国产黄在线观看免费观看不卡| 亚洲午夜久久影院| 免费国产人做人视频在线观看| 日韩电影免费在线观看中文字幕| 亚洲AV无码成人专区| 亚洲伊人久久大香线蕉综合图片| 亚洲免费电影网站|