effective c++條款23:寧以non-member non-friend替換member函式
舉書上的例子:
class WevBrowser
{
public:
WevBrowser(){}
~WevBrowser(){}
public:
void ClearCache();
void ClearHistory();
void RemoveCookies();
};
其中三個成員函式用來分別清除緩衝區、歷史記錄、和cookies。我們都知道,瀏覽器可以一鍵清除以上所有的東西,這是因為這些函式又被封裝在一個函式中:
有兩種方式可以實現封裝:
1. member函式
class WevBrowser { public: WevBrowser(){} ~WevBrowser(){} public: void ClearCache(); void ClearHistory(); void RemoveCookies(); public: void ClearEveryThing(); }; void WevBrowser::ClearEveryThing() { ClearCache(); ClearHistory(); RemoveCookies(); }
2. non-member non-friend函式
void ClearEveryThing(const WevBrowser &wb)
{
wb.ClearCache();
wb.ClearHistory();
wb.RemoveCookies();
}
這兩種方式哪個更好呢?
考慮面向物件守則:它要求資料以及操作資料的那些函式應該被捆綁在一起,這意味著member函式是一個好的選擇,但這是對面向物件守則的一個誤解,面向物件守則要求資料應該儘可能的被封裝,但是由於多了一個member函式,意味著成員變數的封裝性被進一步的破壞,因為每一個member函式都可以訪問其私有成員,而non-member non-friend函式不同,他不可訪問類的私有成員,所以從封裝性的角度來看,non-member non-friend更具封裝性。
雖然non-member non-friend更具封裝性,但是類的內聚性也因此遭到了破壞,為了解決這個缺陷,c++提供了名稱空間,我們可以把類和non-member函式放在同一個名稱空間內:
namespace WebBrowserStuff
{
class WebBrowser{...};
void ClearBrowser(WebBrowser &wb);
}
這樣,既保證了內聚性,又提供了封裝性,而且namespace有一個優點就是它可以跨越多個原始檔:
一個像WebBrowserStuff這樣的類可能擁有大量的便利函式,有些與cookie相關,有些與書籤相關等等,有些人可能只對與書籤相關的便利函式感興趣,那麼他就沒有必要編譯與cookie相關的便利函式,為了將他們分離開,我們可以這樣:
//"WebBrowser.h"
namespace WebBrowserStuff
{
class WebBrowser{...};
... //瀏覽器核心機能
}
//"WebBrowserBookMark.h"
namespace WebBrowserStuff
{
... //書籤便利函式
}
namespace WebBrowserStuff
{
... //cookie便利函式
}
這樣就實現了分離,實際上,c++標準庫的std就是這樣實現的,將vector、list、map等分為獨立的標頭檔案,想用哪個就用哪個,而不是放在同一個標頭檔案中。
有了名稱空間,使用者也可以輕鬆的擴充套件相關的便利函式,只需要新建立一個頭檔案,然後將non-member函式放在相同的名稱空間中即可。