條款28:避免返回handles指向對象內部成分

條款28:避免返回handles指向對象內部成分
    (Avoid returning "handles" to object internals.)

內容:
    這裏所謂的handles一般包括內部對象的references、pointer、iterator,這條款是如何產生的呢?理由是什麼
呢?我先不必急着回答你這些問題,我們先來看一個例子,假設我們的程序中要用到矩形,於是我們寫了Rectangle類去
描述它,該類的內部對象爲矩形左上角(upper left-hand corner)、右下角(Lower right-hand corner)兩個Point
對象,爲了得到這兩個內部對象數據我們提供了upperLeft與lowerRight函數接口.由於這裏Point類是自定義類型,故
我們先開始定義該類:
    class Point{
    public:   
        Point(int x,int y); 
        ...
        void setX(int value);
        void setY(int value);
        ...
    };
    接下來我們開始寫Rectangle類,考慮到讓該類儘可能的小,我決定不把Point點定義在類的內部,而是通過在其內部
放一個輔助類對象指針,該輔助類中存放兩個Point對象.這種做法很常見,下面我們來實現:
    struct RectData{
        Point upperLeftPoint,lowerRightPoint;       
    };
    class Rectangle{
    public:
        ...
        Point& upperLeft()const{ //返回內部對象數據handle
            return pData->upperLeftPoint;
        }
        Point& lowerRight()const{ //返回內部對象數據handle
            return pData->lowerRightPoint;
        }
    private:
        std::tr1::shared_ptr<RectData> pData;
    };
    一看代碼我們就會很輕易的發現這裏存在一個安全隱患:該類提供的兩個接口返回的私有區域內部對象,那麼客戶
使用該類的時候,有可能修改了該內部對象的數據,而實際上我們是不想讓客戶擁有這個權限的.例如:
    Point coord1(0,0);
    Point coord2(100,100);
    const Rectangle rec(coord1,coord2);
    rec.upperLeft().setX(50);//wow, it changes the internal data,that's amazing!
    從這個例子我們至少可以瞭解以下兩點:(1)返回內部對象相當於讓內部對象變爲對用戶可見即相當於讓其處於
public區段中;(2)客戶可以通過返回內部對象handle接口函數修改private對象屬性,這顯然不是我們想要的結果
.怎麼解決?其中的一個解決方案就是在返回類型上加上const即可:
    class Rectangle{
    public:
        ...
        const Point& upperLeft()const{return pData->upperLeftPoint;}
        const Point& lowerRight()const{return pData->lowerRightPoint;}
        ...
    };
    問題似乎得到了解決,我們願意讓客戶看到Rectangle的外圍Points,所以這裏是蓄意放鬆封裝.更重要的是這是
一個有限度的放鬆:這些函數只讓出讀取權,而修改權限仍然是被禁止的.在你說這個解決方案完全perfect之前,這時
客戶默默的寫下了下面這段"恐怖"的代碼:
    class GUIObject{...};//GUI base  class
    const Rectangle boundingBox(const GUIObject& obj);//該函數返回GUI對象的外框.
    現在客戶有可能這麼用這個函數:
    GUIObject* pObject = NULL;
    ... //create GUIObject object
    const Point* pUpperLeft = &( boudingBox(*pObject).upperLeft());
    喔歐,看出問題了沒有?boudingBox返回了一個臨時對象對象(姑且稱之爲tempt),upperLeft返回的是tempt
對象的內部數據的引用,那麼現在pUpperLeft就指向該tempt對象的內部對象的地址.這裏你別忘記囉:該語句調用
結束以後tempt對象自動銷燬,此時的pUpperLeft指向的就是一個不存在的對象.pUpperLeft也就是變成空懸、
懸掛了(dangling)!呵呵.
    今天我們就討論到這裏,如有什麼問題的話,請留言.
    請記住:
    ■ 避免返回handles(包括reference,pointer,iterator)指向內部對象.遵守這個條款可增加封裝性,幫助const
成員函數的行爲像個const,並將發生"虛吊號碼牌"(dangling handles)的可能性降至最低.

發佈了62 篇原創文章 · 獲贊 4 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章