2011-10-19 45 views
15

Meyers在他的着作Effective C++中提到,在某些情況下,非成員非友元函數比成員函數封裝得更好。什麼時候應該更喜歡非會員非朋友功能的會員功能?

例子:

// Web browser allows to clear something 
class WebBrowser { 
public: 
    ... 
    void clearCache(); 
    void clearHistory(); 
    void removeCookies(); 
    ... 
}; 

許多用戶將要執行的所有這些行動起來,所以WebBrowser也可能提供一個功能來做到這一點:

class WebBrowser { 
public: 
    ... 
    void clearEverything(); // calls clearCache, clearHistory, removeCookies 
    ... 
}; 

另一種方法是定義一個非會員非朋友功能。

void clearBrowser(WebBrowser& wb) 
{ 
    wb.clearCache(); 
    wb.clearHistory(); 
    wb.removeCookies(); 
} 

非成員函數更好,因爲「它不會增加可以訪問類的私有部分功能的數量。」,從而導致更好的封裝。

clearBrowser函數是方便的功能因爲他們不能提供任何的功能在一些其他的方式WebBrowser客戶端無法獲得已。例如,如果clearBrowser不存在,則客戶可以自己調用clearCache,clearHistoryremoveCookies

對我來說,便利功能的例子是合理的。但是非會員版擅長的時候除了便利功能還有什麼例子嗎?

更普遍的是,什麼時候使用什麼規則哪個是

+4

我認爲這個話題比宗教實際有更多的宗教信仰。 – PlasmaHH

+1

任何類型的算法都從免費而不是成員中受益,因爲您可以將它們重用於更廣泛的對象。一個例子是添加到新標準中的免費'begin()'/'end()'函數(它允許你迭代靜態數組和容器)。 –

+3

@PlasmaHH:我不認爲它更像是宗教。做一些非會員和非朋友的職能的理由非常合理。 – Nawaz

回答

20

更一般地說,什麼時候使用哪些規則是哪個規則?

這裏是斯科特·邁耶的規則(source):

Scott在打印一篇有趣的文章,其倡導 非成員非友元函數改善類封裝 。他用下面的算法來確定 其中一個函數f被放置:

if (f needs to be virtual) 
    make f a member function of C; 
else if (f is operator>> or operator<<) 
{ 
    make f a non-member function; 
    if (f needs access to non-public members of C) 
     make f a friend of C; 
} 
else if (f needs type conversions on its left-most argument) 
{ 
    make f a non-member function; 
    if (f needs access to non-public members of C) 
     make f a friend of C; 
} 
else if (f can be implemented via C's public interface) 
    make f a non-member function; 
else 
    make f a member function of C; 

他封裝的定義涉及當私有數據 成員改變其影響功能的數量 。

這幾乎總結了一切,這在我看來也是相當合理的。

0

當庫的開發人員想要編寫二元運算符時,通常會使用非成員函數,這些運算符可以在具有類類型的參數上重載,因爲如果您使它們成爲類的成員,則只能在第二個參數(第一個參數是該類的一個對象)。對於complex而言,various arithmetic operators也許是這方面的明確例子。

在你舉的例子中,動機是另一種:使用最少的耦合設計,仍然允許你做的工作。

這意味着,雖然clearEverything可以(而且坦率地說,很可能會成爲)會員,但我們並不是一個人,因爲這在技術上並不是必須的。這給你買兩件事情:

  1. 您不必接受你的公共接口有clearEverything方法的責任(一旦你附帶一個,你就結婚一輩子)。
  2. 有權訪問類的private成員的函數數量較少,因此未來的任何更改都將更容易執行並且不太可能導致錯誤。

這就是說,在這個特殊的例子,我覺得這個概念正在採取太遠了,對於這樣的「無辜」的功能,我倒是傾向於對使它成爲一個成員。但是這個概念是合理的,在「事實並非如此簡單」的「現實世界」情景中,它會更有意義。

3

我經常選擇在我的類之外構建實用方法,當它們是特定於應用程序的時候。

該應用程序通常在不同的上下文中,然後引擎在下面進行工作。如果我們以Web瀏覽器爲例,3種清除方法屬於Web引擎,因爲這是所需的其他功能難以實現的功能,但是,ClearEverything()肯定更適用於特定應用程序。在這種情況下,您的應用程序可能會有一個小對話框,其中包含一個全部清除按鈕以幫助用戶提高效率。也許這不是另一個應用程序重新使用您的Web瀏覽器引擎想要做的,因此讓它在引擎類中會更加混亂。

另一個例子是在數學庫中。通常具有更高級的功能比如平均值或作爲數學類的一部分實現的標準推導是有意義的。但是,如果您有特定於應用程序的方式來計算某種不是標準版本的平均值,那麼它可能應該超出您的類和特定於您應用程序的實用程序庫的一部分。

我從來不是一個強硬編碼規則的忠實粉絲,以某種方式實現事物,這往往是意識形態和原則的問題。

M.

0

局部性和允許類,以提供「足夠」的特點,同時保持封裝有一些事情要考慮。

如果在許多地方重複使用WebBrowser,依賴關係/客戶端可能會定義多個便利功能。這樣可以使你的類(WebBrowser)重量輕,易於管理。

反將了WebBrowser結束了賞心悅目的所有客戶端,只是顯得有些單片獸是難以改變的。

您是否發現該類在多個場景中投入使用後缺乏功能?你的便利功能中出現了哪些模式?最好是(IMO)推遲正式擴展類的接口,直到模式出現,並且有充分理由添加此功能。最簡單的類更容易維護,但您不希望冗餘實現到處都是,因爲這會將維護負擔推到您的客戶端。

如果您的便利函數實現起來很複雜,或者存在一個可以顯着提高性能的常見情況(例如,使用一個鎖清空線程安全集合,而不是每次都使用一次一個鎖來清空線程安全集合),那麼你可能也想考慮這種情況。

在您使用它時,也會出現您認識到WebBrowser確實缺失某些東西的情況。