2012-10-10 66 views
5

在由Bjarne Stroustrup撰寫的The C++ Programming Language中,作者介紹了一個類Matrix,它必須實現一個函數inv()。在第11.5.1節中,他談到了 關於這樣做的兩種可能性。一個是做成員函數,另一個是 做一個朋友函數inv()。然後對部分11.5.2,其中他談到 作出是否使用朋友或成員函數的選擇結束時,他說:當函數應該改變對象的狀態時,是否去成員函數或朋友函數?

如果INV()確實反轉矩陣M本身,而比返回一個新的矩陣 是m的倒數,它應該是一個成員。

這是爲什麼?朋友功能不能改變矩陣的狀態並將 引用返回給矩陣?是因爲當我們調用該函數時可能會傳遞一個臨時矩陣 ..

+1

可能在對象內部有更清晰定義的功能封裝,而不是在不同的朋友功能中傳播。 – PherricOxide

+2

相關http://stackoverflow.com/a/7821482/46642 –

+0

我認爲這裏值得讚揚的答案應該指出成員函數和非成員朋友之間的實際差異,並解釋這些差異如何有助於作出決定,而不是用「因爲它提供更多/更少的封裝」而將所有東西都浪費掉了。 –

回答

9

說實話,我認爲做出這樣一個決定的唯一原因是語法方便和傳統。我會解釋爲什麼通過展示什麼是(而非)兩者之間的差異,以及在做出決定時這些差異如何重要。

非會員朋友功能和公共會員功能有什麼不同?不多。畢竟,成員函數只是一個具有隱藏this參數的常規函數​​,並且可以訪問該類的私有成員。

// what is the difference between the two inv functions? 
// --- our code --- 
struct matrix1x1 { // this one is simple :P 
private: 
    double x; 
public: 
    //... blah blah 
    void inv() { x = 1/x; } 
    friend void inv(matrix1x1& self) { self.x = 1/self.x; } 
}; 
matrix1x1 a; 

// --- client code --- 

// pretty much just this: 
a.inv(); 
// vs this: 
inv(a); 

void lets_try_to_break_encapsulation(matrix1x1& thingy) { 
    thingy.x = 42; // oops, error. Nope, still encapsulated. 
} 

它們都提供相同的功能,並且決不會改變其他功能可以做的事情。相同的內部結構暴露於外部世界:封裝方面沒有區別。其他函數完全可以做不同的事情,因爲有一個修改私有狀態的朋友函數。實際上,大多數函數都可以編寫非會員朋友函數(虛函數和一些重載操作符必須是成員),以提供完全相同的封裝量:用戶不能編寫任何其他朋友函數而不修改除了朋友功能以外,沒有其他功能可以訪問私人成員。我們爲什麼不這樣做?因爲它會違背99.99%的C++程序員的風格,並且沒有很大的優勢可以從中獲益。

不同之處在於功能的性質和您稱之爲的方式。作爲成員函數意味着你可以從它獲得一個指向成員函數的指針,並且作爲一個非成員函數意味着你可以得到一個指向它的函數指針。但這很少相關(特別是使用通用函數包裝器,如std::function)。

其餘的區別是句法。 D語言的設計者決定統一整個事情,並說你可以直接調用一個成員函數,如inv(a),然後調用一個自由函數作爲它的第一個參數的成員,如a.inv()。沒有任何班級因爲這個或者任何東西而突然被封裝得不好。

要解決問題中的特定示例,inv應該是成員還是非成員?我可能會讓它成爲一個成員,因爲我上面概述的相似性論點。非風格化,它沒有區別。


。這在C++中不太可能發生,因爲在這一點上,這將是一個突破性改變,因爲沒有實質性的好處。對於一個極端的例子,它會打破我上面寫的matrix1x1類,因爲它使得兩個調用都不明確。

+0

你已經提供了一個完全包含好友功能的例子,但是當有人讓一個班級成爲朋友時呢?可以說這確實減少甚至破壞封裝。 – Brady

+0

關於成員函數vs朋友函數的問題值得討論(它對於朋友類沒有任何意義)。如果是關於課程,我仍然需要一個例子來接受這個聲明。 –

+0

好點。我的回答是指一般的朋友(班級和功能)。 STL在幾個地方使用好友功能,但我不認爲它使用好友類。 – Brady

0

OOD(C++試圖提升)所固有的封裝理念規定對象狀態只能從內部修改。 它在語法上是正確的(編譯器允許它),但應該避免它。

如果不使用預定義的接口,整個系統中的元素會彼此改變是很容易出錯的。 對象存儲和功能可能會改變,並且尋找使用對象的特定部分的代碼(可能很大)將是一場噩夢。

0

有關於使用朋友兩個對立的論點:

一邊說,朋友減少了封裝,因爲現在你讓外部實體訪問一個類的內部和內部只能由成員方法進行修改。

另一方面說朋友實際上可以增加封裝,因爲您可以將一個類的內部訪問權限授予一小組外部實體,從而避免了將內部類屬性公開給所有人的需要,因此這些外部實體可以訪問/操作它們。

雙方都可以爭論,但我傾向於同意第一個選項。

至於你的問題,其作爲PherricOxide在他的評論中提到:如果一個類的內部屬性需要修改,它更好的是由成員方法完成,從而強制封裝。這與上面提到的第一個選項是一致的。

+0

爲什麼downvote ?? ?? – Brady

相關問題