1. 程式人生 > >effective c++條款23:寧以non-member non-friend替換member函式

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函式放在相同的名稱空間中即可。