簡體   English   中英

非會員非朋友功能與私人功能

[英]Non-member non-friend functions vs private functions

Herb Sutter表示,在C ++中編寫方法的最面向對象的方法是使用非成員非友元函數。 這是否意味着我應該采用私有方法並將它們變成非成員非朋友函數? 這些方法可能需要的任何成員變量都可以作為參數傳入。

示例(之前):

class Number {
 public:
  Number( int nNumber ) : m_nNumber( nNumber ) {}
  int CalculateDifference( int nNumber ) { return minus( nNumber ); }
 private:
  int minus( int nNumber ) { return m_nNumber - nNumber; }
  int m_nNumber;
};

示例(后):

int minus( int nLhsNumber, int nRhsNumber ) { return nLhsNumber - nRhsNumber; }
class Number {
 public:
  Number( int nNumber ) : m_nNumber( nNumber ) {}
  int CalculateDifference( int nNumber ) { return minus( m_nNumber, nNumber ); }
 private:
  int m_nNumber;
};

我是在正確的軌道上嗎? 是否應將所有私有方法移至非成員非朋友函數? 什么應該是規則,否則會告訴你?

我相信自由功能並同意Sutter,但我的理解是相反的。 並不是說你的公共方法應該依賴於自由函數而不是私有方法,而是你可以使用提供的公共接口在類之外用自由函數構建更豐富的接口。

也就是說,你不要將你的私有部分推到類之外,而是將公共接口減少到最小,允許你以盡可能少的耦合構建其余的功能:只使用公共接口。

在您的示例中,如果可以根據其他操作有效地表示,我將在類外部移動的是CalculateDifference方法。

class Number { // small simple interface: accessor to constant data, constructor
public:
  explicit Number( int nNumber ) : m_nNumber( nNumber ) {}
  int value() const { return m_nNumber; }
private:
  int m_nNumber;
};
Number operator+( Number const & lhs, Number const & rhs ) // Add addition to the interface
{
   return Number( lhs.value() + rhs.value() );
}
Number operator-( Number const & lhs, Number const & rhs ) // Add subtraction to the interface
{
   return Number( lhs.value() - rhs.value() );
}

優點是,如果你決定重新定義你的數字內部(你可以用這么簡單的類做的那么多),只要你保持公共接口不變,那么所有其他功能都可以開箱即用。 內部實現細節不會強制您重新定義所有其他方法。

困難的部分(不是上面的簡單示例)是確定您必須提供的最少接口。 從上一個問題引用的文章( GotW#84 )就是一個很好的例子。 如果你仔細閱讀它,你會發現在保持相同的功能和性能的同時,你可以大大減少std :: basic_string中的方法數量。 計數將從103個成員函數下降到只有32個成員。 這意味着類中的實現更改將僅影響32個而不是103個成員,並且由於保留了接口,因此不需要更改可以實現32個成員的其余功能的71個自由函數。

這一點非常重要:它更加封裝,因為您限制了實現更改對代碼的影響。

移出原始問題,這是一個簡單的例子,說明如何使用自由函數改進類的更改的局部性。 假設一個具有非常復雜的加法運算的復雜類。 你可以去實現它並將所有運算符覆蓋實現為成員函數,或者你可以在內部輕松有效地實現其中的一些,並將其余部分作為自由函數提供:

class ReallyComplex
{
public:
   ReallyComplex& operator+=( ReallyComplex const & rhs );
};
ReallyComplex operator+( ReallyComplex const & lhs, ReallyComplex const & rhs )
{
   ReallyComplex tmp( lhs );
   tmp += rhs;
   return tmp;
}

可以很容易地看出,無論原始operator+=如何執行其任務,自由operator+都能正確執行其任務。 現在,對於類的任何和所有更改,必須更新operator+= ,但外部operator+將在其余生中保持不變。

上面的代碼是一個常見的模式,雖然通常不是通過常量引用接收lhs操作數並在其中創建臨時對象,但可以更改它以使參數本身成為值副本,從而幫助編譯器進行一些優化:

ReallyComplex operator+( ReallyComplex lhs, ReallyComplex const & rhs )
{
   lhs += rhs;
   return lhs;
}

並非所有私有方法都應該移動到非成員非友元函數,但那些不需要訪問私有數據成員的函數應該是。 你應該盡可能少地訪問,盡可能地封裝你的類。

我強烈建議您閱讀Scott Meyers的Effective C ++,它解釋了為什么要這樣做以及何時適當。

編輯:我想補充說,對於私有方法然后對於公共方法來說這不太正確,盡管仍然有效。 由於封裝與您將通過修改方法而破解的代碼量成比例,具有私有成員函數,即使它不需要訪問數據成員。 這是因為修改代碼會破壞很少的代碼,只會破壞你可以控制的代碼。

在回答不同的問題時,似乎已經解決了這個問題。

這樣的規則往往是好的仆人和壞的主人,有涉及的交易。 如果您應用您建議的轉換,結果是否更加可維護? 為什么? 我相信預期的好處是通過減少直接處理對象的私有數據的方法的數量,您可以更容易地理解它的行為,從而使其更容易維護。

我不相信你的后例能達到這個目標。 [您可能會注意到上面的“之后”示例無論如何都不會編譯,但這是另一個故事。]如果我們調整它以純粹根據公共方法而不是內部狀態實現外部函數(為值添加一個訪問器)然后我想我們已經有了一些收獲。 是否足以保證工作。 我的意見:沒有。 我認為當方法更新對象中的數據值時,所提出的移動到外部函數的好處變得更大。 我想要實現少數不變的維護變異器並實現主要的“業務”方法 - 外部化這些方法是確保它們只能在變更器方面工作的一種方式。

它在很大程度上取決於您的設計和情況。當我編寫代碼時,我實際上只將publicprotected函數放入類中。 其余的是實現細節,它是.cpp文件的一部分。

我經常使用pImpl習語。 在我看來,這兩種方法都有以下好處:

  • 清潔界面設計

    處理您的界面的用戶將更好地理解它,而不必深入研究實現細節,如果他們不需要。

  • 與實現脫鈎

    如果在不更改接口的情況下更改.cpp文件中的某些內容,則只需重新編譯一個.cpp文件(編譯單元)並重新鏈接該應用程序,從而縮短構建時間。

  • 隱藏使用您的界面的其他類的依賴項

    如果所有內容都放入頭文件中,您有時必須包含其他標題,這些標題是“界面的一部分”,而其他標題則包括它們,無論它們是否不想。 將實際實現放到編譯單元將隱藏這些依賴項。

但有時你會編寫只有頭的庫或實現,因此你不能把東西放到編譯單元中,因為這種方法不僅需要包含你的lib,還需要鏈接你的lib。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM