程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> Item 28:不要返回對象內部的句柄 Effective C++筆記

Item 28:不要返回對象內部的句柄 Effective C++筆記

編輯:關於C++

Item 28: Avoid returning handles to object internals.

不要返回對象私有成員的句柄。這裡的“句柄”(handle)包括引用、指針和迭代器。 這樣可以增加類的封裝性、使得const函數更加const, 也避免了空引用的創建(dangling handles)。

為了方便起見,下文中統一用指針來稱呼這三類句柄。

返回私有成員的指針

在繼續Scott Meyers的討論之前,先來回顧一下類成員指針的行為。 首先如果不加限制,直接返回私有成員的指針會導致私有成員被完全暴露。例如:

class Rectangle {
  int _left, _top;
public:
  int& left() { return _left; }
};
Rectangle rec;
rec.left() = 3;   // rec._left被修改

其實這已經足以說明返回私有成員指針相當於完全暴露了私有成員。

暴露私有成員為常量

如果是為了保護私有成員不被修改,只是為了讓外界可以不通過函數就可以訪問_left, 可以將left()聲明為const

int& left() const{ return _left; }

上述代碼還有問題~ 編譯器會產生如下錯誤:

error: binding of reference to type 'int' to a value of type 'const int'
      drops qualifiers

這是因為常量方法不能修改當前對象,其返回值也應該是const的。應該這樣寫:

const int& left() const{ return _left; }

由於返回值聲明了const,客戶修改內部變量便會編譯錯了。我們成功地在開放了內部變量的同時防止了內部變量被修改。但是問題沒有到此為止!還記得嗎?C++的常量定義為bitwise constness,只要當前對象沒被修改就算常量。所以如果我們將lefttop存在類的外面,常量方法的返回值類型檢查便會失效,比如把數據存在Point裡面:

class Point{
public:
    int left, right;
};
class Rectangle {
    Point* p;
public:
    int& left() const { return p->left; }
};
...
const Rectangle rec;
rec.left() = 3;     // rec明明是const對象,但我們可以修改它~

現在Rectangle的大小是sizeof(void*)(指針大小), 即p->left並不在這個對象的內存裡, 因而常量方法的返回值可以不聲明const。 這時客戶便可以通過這個返回的left來修改對象私有成員了。 所以返回對象內部的指針,會導致常量方法的const屬性被破壞。 根本原因在於C++的bitwise constness語法檢查風格。

注意如果p被聲明為Point而非Point*時,p->left仍處於當前類的內存區域內, 編譯器會要求left() const返回值為const int&

空懸指針問題

可能你已經注意到了,我們完全可以稍微改善一下上面的代碼來實現私有成員的寫保護。 既然是由於bitwise constness編譯器不提供類型檢查, 我們手動限制返回值為const即可:

const int& left() const{ return p->left; }

很多情況下問題確實是這樣解決的,比如實現operator[]() const時。 但下標運算符只是一個特例,一般情況我們是不會返回內部指針的。 因為返回的指針和擁有者對象具有同樣的生命周期, 返回的指針很容易被懸空,比如有一個返回Rectanglebounding函數:

const Rectangle bounding();

我們希望獲得那個Rectangleleft,可能會這樣寫:

const int& left = bounding().left();

問題在哪裡呢?left被懸空了,它並沒有保持left的值! 因為bounding()返回的對象沒有賦值給任何變量它是一個臨時對象。 臨時對象在語句執行後立即銷毀,那個私有成員的引用也將失效。


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved