Effective C 條款23 第4章

2021-07-05 02:41:44 字數 2966 閱讀 9021

prefer non-member non-friend functions to member functions

想象有個 class 用來表示網頁瀏覽器,這樣的 class 可能提供眾多函式,如下所示:

class webbrowser ;
許多使用者會想一整個執行所有這些動作,因此webbrowser也提供這樣乙個函式:

class webbrowser ;
當然,這一機能也可由乙個non-member函式呼叫適當的member函式而提供:

void clearbrowser(webbrowser& wb)
那麼,

哪乙個比較好呢?是member函式cleareverything還是non-member函式clearbrowser?

物件導向守則要求,資料以及運算元據的那些函式應該被**在一起,這意味它建議member函式是較好的選擇.不幸的是這個建議不正確.物件導向守則要求資料應該盡可能被封裝,然而與直觀相反,member函式cleareverything帶來的封裝性比non-member函式clearbrowser低.此外,提供non-member函式可允許對webbrowser相關機能有較大的包括彈性,而那最終導致較低的編譯相依度,增加webbrowser的可延展性.因此許多方面non-member做法比member做法好.

從封裝開始討論.如果某些東西被封裝,它就不再可見.愈多東西被封裝,愈少人可以看到它.而愈少人看到它,就有愈大的彈性去變化它,因為改變僅僅直接影響看到改變的那些事物.因此這就是封裝重要的乙個原因:它使得能夠改變事物而只影響有限客戶.

考慮物件內的資料,愈少**可以看到資料(訪問它),愈多的資料可被封裝.如何測量"有多少**可以看到某一塊資料"呢?計算能夠訪問該資料的函式數量,作為一種粗糙的測量.愈多函式可訪問它,資料的封裝性就愈低.

條款22指出,成員變數應該是 private,如果它們不是,就有無限量的函式可以訪問它們,它們就毫無封裝性.能夠訪問 private 成員變數的函式只有 class 的member函式加上 friend 函式而已.如果在乙個member函式和乙個non-member,non-friend 函式之間做抉擇,而且兩者提供相同的機能,那麼導致較大封裝性的是non-member non-friend 函式,因為它並不增加"能夠訪問class內的private成分"的函式數量.這也就解釋了clearbrowser(non-member non-friend 函式)比cleareverything(member函式)更受歡迎:它導致webbrowser class 有較大的封裝性.

在這一點有兩件事值得注意.第一,這個論述只適用於non-member non-friend 函式.第二,封裝性讓函式"成為class的non-member",並不意味著它"不可以是另乙個class的member".例如可以令clearbrowser成為某工具類的乙個 static member函式.只要它不是webbrowser的一部分,就不會影響webbrowser的 private 成員封裝性.

在c++,比較自然的做法是讓clearbrowser成為乙個non-member函式並且位於webbrowser所在的同乙個 namespace 內:

namespace webbrowserstuff ;

void clearbrowser(webbrowser& wb);

...}

然而,這不只是為了看起來自然而已.要知道

namespace 和 class 不同,前者可跨越多個原始碼檔案而後者不能.因為像clearbrowser這樣的函式是個"提供便利的函式",如果它既不是members也不是 friend,就沒有對webbrowser的特殊訪問權力,也就不能提供"webbrowser客戶無法以其他方式取得"的機能.舉個例子,如果clearbrowrer不存在,客戶端只好自行呼叫clearcache,clearhistory和removecookie.

乙個像webbrowser這樣的 class 可能擁有大量便利函式,例如某些與書籤相關,某些與cookie管理有關...通常客戶只對其中某些感興趣.沒道理乙個只對書籤相關便利函式感興趣的客戶卻與乙個cookie相關便利函式發生編譯相依關係.分離它們的最直接做法是將書籤相關便利函式宣告於乙個標頭檔案,將cookie相關便利函式宣告於另乙個標頭檔案...依次類推:

// 標頭檔案"webbrowser.h"--這個標頭檔案針對class webbrowser自身以及webbrowser核心機能

namespace webbrowserstuff ;

... // 核心機能,例如幾乎所有客戶都需要的non-member函式

}// 標頭檔案"webbrowserbookmark.h"

namespace webbrowserstuff

// 標頭檔案"webbrowsercookies.h"

namespace webbrowserstuff

注意,這正是c++標準程式庫的組織方式.標準程式庫有數十個標頭檔案(,,等等),每個標頭檔案宣告std的某些機能.如果客戶只想使用vector相關機能,他不需要#include.如果客戶不想使用list,也不需要#include.這允許客戶只對他們所用的那一部分系統形成編譯相依(詳見條款31,其中討論降低編譯依存性的其他做法).以這種方式切割機能並不適用於 class 成員函式,因為 class 必須整體定義,不能被分割為片片斷斷.

注意:

寧可拿non-member non-friend 函式替換member函式.這樣做可以增加封裝性,包裹彈性和機能擴充性.

Effective C 條款8 第2章

prevent exception from leving destructors.c 並不禁止析構函式吐出異常,但它不鼓勵這樣做.這是有原因的,考慮以下 class widget 假設這個可能吐出乙個異常 void dosomething v在這裡被自動銷毀當vector v被銷毀,它有責任銷毀其...

Effective C 條款15 第3章

provide access to raw resources in resources managing classes 資源管理類 resource managing classes 很棒.它們是對抗資源洩露的堡壘.在乙個良好的環境中將依賴這樣的classes來處理和資源之間的所有互動.而不是直...

Effective C 條款24 第4章

令 class 支援隱式型別轉換通常是個糟糕的主意,當然這條規則有其例外,最常見的例外是在建立數值型別時.假設設計乙個 class 用來表現有理數,允許整數 隱式轉換 為有理數似乎頗為合理.的確,它並不比c 內建從 int 至 double 的轉換來得不合理.假設這樣開始rational class...