国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

C++箴言:避免返回對象內部構件的句柄

2019-11-17 05:40:03
字體:
來源:轉載
供稿:網友

  假設你正在一個包含矩形的應用程序上工作。每一個矩形都可以用它的左上角和右下角表示出來。為了將一個 Rectangle 對象保持在較小狀態,你可能決定那些點的定義的域不應該包含在 Rectangle 本身之中,更合適的做法是放在一個由 Rectangle 指向的輔助的結構體中:

class Point {
  // class for rePResenting points
  public:
   Point(int x, int y);
   ...

   void setX(int newVal);
   void setY(int newVal);
   ...
};

strUCt RectData {
  // Point data for a Rectangle
  Point ulhc; // ulhc = " upper left-hand corner"
  Point lrhc; // lrhc = " lower right-hand corner"
};

class Rectangle {
  ...

  private:
   std::tr1::shared_ptr<RectData> pData; // see Item 13 for info on
}; // tr1::shared_ptr

  由于 Rectangle 的客戶需要有能力操控 Rectangle 的區域,因此類提供了 upperLeft 和 lowerRight 函數。可是,Point 是一個用戶定義類型,所以,在典型情況下,以傳引用的方式傳遞用戶定義類型比傳值的方式更加高效的觀點,這些函數返回引向底層 Point 對象的引用:

class Rectangle {
  public:
   ...
   Point& upperLeft() const { return pData->ulhc; }
   Point& lowerRight() const { return pData->lrhc; }
   ...
};

  這個設計可以編譯,但它是錯誤的。實際上,它是自相矛盾的。一方面,upperLeft 和 lowerRight 是被聲明為 const 的成員函數,因為它們被設計成僅僅給客戶提供一個獲得 Rectangle 的點的方法,而不答應客戶改變這個 Rectangle。另一方面,兩個函數都返回引向私有的內部數據的引用——調用者可以利用這些引用修改內部數據!例如: Point coord1(0, 0);

Point coord2(100, 100);

const Rectangle rec(coord1, coord2); // rec is a const rectangle from
// (0, 0) to (100, 100)

rec.upperLeft().setX(50); // now rec goes from
// (50, 0) to (100, 100)!

  請注重這里,upperLeft 的調用者是怎樣利用返回的 rec 的內部 Point 數據成員的引用來改變這個成員的。但是 rec 卻被期望為 const!

  這直接引出兩條經驗。第一,一個數據成員被封裝,但是具有最高可訪問級別的函數還是能夠返回引向它的引用。在當前情況下,雖然 ulhc 和 lrhc 被聲明為 private,它們還是被有效地公開了,因為 public 函數 upperLeft 和 lowerRight 返回了引向它們的引用。第二,假如一個 const 成員函數返回一個引用,引向一個與某個對象有關并存儲在這個對象本身之外的數據,這個函數的調用者就可以改變那個數據(這正是二進制位常量性的局限性的一個副作用)。

  我們前面做的每件事都涉及到成員函數返回的引用,但是,假如它們返回指針或者迭代器,因為同樣的原因也會存在同樣的問題。引用,指針,和迭代器都是句柄(handle)(持有其它對象的方法),而返回一個對象內部構件的句柄總是面臨危及對象封裝安全的風險。就像我們看到的,它同時還能導致 const 成員函數改變了一個對象的狀態。

  我們通常認為一個對象的“內部構件”就是它的數據成員,但是不能被常規地公開訪問的成員函數(也就是說,它是 protected 或 private 的)也是對象內部構件的一部分。同樣地,不要返回它們的句柄也很重要。這就意味著你絕不應該有一個成員函數返回一個指向擁有較小的可訪問級別的成員函數的指針。假如你這樣做了,它的可訪問級別就會與那個擁有較大的可訪問級別的函數相同,因為客戶能夠得到指向這個擁有較小的可訪問級別的函數的指針,然后就可以通過這個指針調用這個函數。

  無論如何,返回指向成員函數的指針的函數是難得一見的,所以讓我們把注重力返回到 Rectangle 類和它的 upperLeft 和 lowerRight 成員函數。我們在這些函數中挑出來的問題都只需簡單地將 const 用于它們的返回類型就可以排除:

class Rectangle {
public:
...
const Point& upperLeft() const { return pData->ulhc; }
const Point& lowerRight() const { return pData->lrhc; }
...
};

  通過這個修改的設計,客戶可以讀取定義一個矩形的 Points,但他們不能寫它們。這就意味著將 upperLeft 和 upperRight 聲明為 const 不再是一句空話,因為他們不再答應調用者改變對象的狀態。至于封裝的問題,我們總是故意讓客戶看到做成一個 Rectangle 的 Points,所以這是封裝的一個故意的放松之處。更重要的,它是一個有限的放松:只有讀訪問是被這些函數答應的,寫訪問依然被禁止。

  雖然如此,upperLeft 和 lowerRight 仍然返回一個對象內部構件的句柄,而這有可能造成其它方面的問題。非凡是,這會導致空懸句柄:引用了不再存在的對象的構件的句柄。這種消失的對象的最普通的來源就是函數返回值。例如,考慮一個函數,返回在一個矩形窗體中的 GUI 對象的 bounding box:

class GUIObject { ... };

const Rectangle // returns a rectangle by
boundingBox(const GUIObject& obj); // value; see Item 3 for why
// return type is const

  現在,考慮客戶可能會這樣使用這個函數:

GUIObject *pgo; // make pgo point to
... // some GUIObject

const Point *pUpperLeft = // get a ptr to the upper
&(boundingBox(*pgo).upperLeft()); // left point of its
// bounding box

  對 boundingBox 的調用會返回一個新建的臨時的 Rectangle 對象。這個對象沒有名字,所以我們就稱它為 temp。于是 upperLeft 就在 temp 上被調用,這個調用返回一個引向 temp 的一個內部構件的引用,非凡是,它是由 Points 構成的。隨后 pUpperLeft 指向這個 Point 對象。到此為止,一切正常,但是我們無法繼續了,因為在這個語句的末尾,boundingBox 的返回值—— temp ——被銷毀了,這將間接導致 temp 的 Points 的析構。接下來,剩下 pUpperLeft 指向一個已經不再存在的對象;pUpperLeft 空懸在創建它的語句的末尾!

  這就是為什么任何返回一個對象的內部構件的句柄的函數都是危險的。它與那個句柄是指針,引用,還是迭代器沒什么關系。它與是否受到 cosnt 的限制沒什么關系。它與那個成員函數返回的句柄本身是否是 const 沒什么關系。全部的問題在于一個句柄被返回了,因為一旦這樣做了,你就面臨著這個句柄比它引用的對象更長壽的風險。

  這并不意味著你永遠不應該讓一個成員函數返回一個句柄。有時你必須如此。例如,Operator[] 答應你從 string 和 vector 中取出單獨的元素,而這些 operator[]s 就是通過返回引向容器中的數據的引用來工作的——當容器本身被銷毀,數據也將銷毀。盡管如此,這樣的函數屬于特例,而不是慣例。

  Things to Remember

  ·避免返回對象內部構件的句柄(引用,指針,或迭代器)。這樣會提高封裝性,幫助 const 成員函數產生 cosnt 效果,并將空懸句柄產生的可能性降到最低。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 肃宁县| 鄂尔多斯市| 铜陵市| 巧家县| 泗洪县| 卫辉市| 淮北市| 衡南县| 台东县| 延边| 两当县| 和硕县| 沙湾县| 大埔县| 开封市| 宜昌市| 集安市| 西林县| 灌云县| 文安县| 吉木萨尔县| 岳阳县| 灵台县| 巢湖市| 津市市| 南漳县| 乐陵市| 博湖县| 吉林省| 仪征市| 博客| 印江| 旬邑县| 赫章县| 台前县| 崇礼县| 滁州市| 灵宝市| 临清市| 宁都县| 凌海市|