程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 讀書筆記 effective c++ Item 23 寧可使用非成員非友元函數函數也不使用成員函數

讀書筆記 effective c++ Item 23 寧可使用非成員非友元函數函數也不使用成員函數

編輯:關於C++

讀書筆記 effective c++ Item 23 寧可使用非成員非友元函數函數也不使用成員函數。本站提示廣大學習愛好者:(讀書筆記 effective c++ Item 23 寧可使用非成員非友元函數函數也不使用成員函數)文章只能為提供參考,不一定能成為您想要的結果。以下是讀書筆記 effective c++ Item 23 寧可使用非成員非友元函數函數也不使用成員函數正文


1. 非成員非友元好還是成員函數好?

想象一個表示web浏覽器的類。這樣一個類提供了清除下載緩存,清除URL訪問歷史,從系統中移除所有cookies等接口:

 

 1 class WebBrowser {
 2 
 3 public:
 4 
 5 ...
 6 
 7 void clearCache();
 8 
 9 void clearHistory();
10 
11 void removeCookies();
12 
13 ...
14 
15 };

 

許多用戶想將這些動作一塊執行,所以web浏覽器為此可以提供一個函數:

 1 class WebBrowser {
 2 
 3 public:
 4 
 5 ...
 6 
 7 void clearEverything(); // calls clearCache, clearHistory,
 8 
 9 // and removeCookies
10 
11 ...
12 
13 };

 

當然,這個功能也可以通過非成員函數來提供,讓它調用合適的成員函數就可以了:

 1 void clearBrowser(WebBrowser& wb)
 2 
 3 {
 4 
 5 wb.clearCache();
 6 
 7 wb.clearHistory();
 8 
 9 wb.removeCookies();
10 
11 }

 

哪種方法才是更好的呢?是成員函數clearEverying還是非成員函數clearBrower?

2. 為什麼非成員非友元函數好?

面向對象准則指出數據以及操作數據的函數應該被捆綁到一起,這就表明它建議成員函數是更好的選擇。不幸的是,這個建議是不正確的。它曲解了面向對象的含義。面向對象准則指出數據應該盡可能的被封裝。違反直覺的是,成員函數clearEverything實際上並沒有比非成員函數clearBrower有更好的封裝性。並且提供非成員函數能夠為web浏覽器的相關功能提供更大的包裝(packaging)靈活性,相應的,就可以產生更少的編譯依賴和更好的可擴展性。因此非成員函數比成員函數在許多方面都要好。明白為什麼很重要。

2.1 用非成員非友元能產生更具封裝性的類

以封裝開始。如果一些東西被封裝了,就意味著被隱藏起來了。封裝的東西越多,就有更少的客戶能看到它們。更少的客戶能看到它們就意味著我們有更大的靈活性來進行對它們進行修改,因為我們的修改直接影響的是能看到這些修改的客戶。因此封裝性越好,就賦予我們更大的能力來對其進行修改。這也是我們將封裝擺在第一位的原因:它以一種只影響有限數量的客戶的方式為我們修改東西提供了靈活性。

 

考慮同一個對象相關聯的數據。看到這些數據的代碼越少(也就是可訪問它),數據就被封裝的越好,我們就有更大的自由來修改這個對象的數據的一些特征,像數據成員的數量,類型等等。通過確認有多少代碼能夠看到數據來判斷數據的封裝性是粗粒度的方法,我們可以計算出能夠訪問數據的函數的數量,能訪問數據的函數越多,封裝性越差。

 

Item 22解釋了數據成員應該是private的,因為如果不是,未限定數量的成員函數就能夠訪問它們。這樣就根本沒有封裝性可言。對於private的數據成員,能夠訪問它們的函數的數量為所在類的成員函數的數量加上友元函數的數量,因為只有成員函數和友元函數能夠訪問private成員。考慮在一個成員函數(不僅能訪問類的private數據,也能訪問private函數,enums,typedef等等)和一個非成員非友元函數(私有的數據和函數都不能訪問)之間做一個選擇,它們提供了相同的功能,能夠產生更大封裝性的選擇是非成員非友元函數,因為他們沒有增加能夠訪問類私有部分的函數的數量。這就解釋了為什麼clearBrower(非成員非友元函數)要優於clearEverything:在WebBrowser類中,它產生了更大的封裝。

在這點上有兩件事情需要注意。第一,這個論述只適用於非成員非友元函數。友元同成員函數對類的私有成員有相同的訪問權,因此對封裝有相同的影響。從封裝的觀點來看,不是在成員和非成員函數之間進行選擇,而是在成員和非成員非友元函數之間進行選擇。(封裝當然不是僅有的選擇視角,Item 24中解釋了在隱式類型轉換中,需要在成員和非成員函數之間做出選擇。)

第二件需要注意的事情恰恰是因為封裝性指明類的函數為非成員函數這個觀點,這並不意味著這個函數不能是別的類的成員函數。我們可以將clearBrower聲明成一個utility類的靜態成員函數。只要它不是WebBrowser的一部分(或者一個友元),它就不會影響WebBrower的private成員的封裝性。

2.2 用非成員非友元可以減少編譯依賴

在c++中,一個更加自然的方法是使clearBrower成為同WebBrowser有相同命名空間的非成員函數:

1 namespace WebBrowserStuff {
2 
3 class WebBrowser { ... };
4 
5 void clearBrowser(WebBrowser& wb);
6 
7 ...
8 
9 }

 

不僅僅是更加自然,因為命名空間不像類,它是可以跨文件的。這是很重要的,因為像clearBrower這樣的函數是很便利的函數。既不是成員也不是友元,對WebBrower類沒有特殊訪問權,因此它不能提供WebBrowser客戶沒有獲取到的其他任何功能。舉個例子,如果clearBrower這個函數不存在,客戶只好自己調用clearCache,clearHistory,和removeCookies。

像webBrower這樣的類可以有大量的便利函數,一些和標簽相關,另一些和打印相關還有一些和cookie管理相關等等。通常大多數客戶只對其中的一部分有興趣。沒有理由讓只對標簽便利函數感興趣的客戶編譯依賴於cookie相關的便利函數。將它們分開的直接的方法是將它們聲明在不同的頭文件中。

 1 // header “webbrowser.h” — header for class WebBrowser itself
 2 
 3 // as well as “core” WebBrowser-related functionality
 4 
 5 namespace WebBrowserStuff {
 6 
 7 class WebBrowser { ... };
 8 
 9 ... // “core” related functionality, e.g.
10 
11 // non-member functions almost
12 
13 // all clients need
14 
15 }
16 
17 // header “webbrowserbookmarks.h”
18 
19 namespace WebBrowserStuff {
20 
21 ... // bookmark-related convenience
22 
23 } // functions
24 
25 // header “webbrowsercookies.h”
26 
27 namespace WebBrowserStuff {
28 
29 ... // cookie-related convenience
30 
31 } // functions

 

注意標准C++庫就是這麼組織的。它並沒有在std命名空間中將所有東西包含在一個單一的<C++ Stand Library>頭文件中,而是有許多頭文件(<vector>,<algorithm>,<memory>等等),每個頭文件聲明了std命名空間中的一部分功能。只使用vector相關功能客戶不需要#include <memory>;不需要使用list的客戶不必#include <list>。這就允許客戶只編譯依賴於它們實際用到的部分。(Item 31中討論了減少編譯依賴的其他方法)。當一個功能來源於一個類的成員函數,那麼將其分割就是不可能的,因為一個類必須被定義在一個整體中。它不能再分了。

2.2 用非成員非友元可以更好的提供擴展性

 將所有的便利函數放在不同的頭文件中——但放在一個命名空間中——同樣意味著客戶可以很容易的對便利函數進行擴展。他們需要做的是向命名空間中添加更多的非成員非友元函數。舉個例子,如果一個WebBrower客戶決定實現圖片下載相關的便利函數,他只需要創建一個頭文件,在命名空間WebBrowserStuff中將這些函數進行聲明。新函數能像舊的函數一樣同它們整合在一起。這也是類不能提供的另外一個性質,因為客戶是不能對類定義進行擴展的。當然,客戶可以派生出新類,但派生類沒有權限訪問基類的封裝成員(像private成員),這樣的“擴展功能”就是二等身份。此外,正如Item 7中解釋的,並不是所有類都被設計成基類。

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