夥伴函式和成員函式差在哪裡?為什麼輸入/輸出的多載運算子一定要為夥伴函式?如果A類別把B函式當作夥伴,但B函式的參數中沒有A類別,顯然這個B不把A當夥伴,這會造成main()無法找到B函式。
還是很困惑嗎?文章裡有答案喔~😎
一、夥伴 (friend)
(一)函式
雖然friend函式的函式原型宣告在類別中,但它不是成員函式。如果介面和實作是合在一起的話,friend函式是定義在類別外面;而如果介面和實作分開,成員函式必須加上範圍解析子::,但非成員函式不需要,這也包含friend函式。
也就是friend函式的定義是獨立於類別的,並且如果friend函式想要獲得public資料成員的存取權,friend函式不能主動設定,並需類別主動給予friend函式權限。
有些不支持friend函式的原因,覺得friend的機制會破壞OOP的基本原則。
(二)多載函式 (overloading)
1. 輸入輸出
<<, >> 多載時,一定要宣告成friend,因為我們不能動ostream物件的東西,故只能授權ostream能存取新物件的private成員。且傳參考給運算子,也回傳參考(lvalue),這樣就可以連續輸入/輸出,不可以回傳區域參考,不然會消失!這點和指標一樣~
2. 多載成員函式 v.s. 多載夥伴函式
- cost + tax
- cost.operator+(tax) //member function, cost must be an object,使用上比較沒有彈性
- operator+(cost, tax) // non-member function,使用上比較有彈性
(三)類別 (class)
friend類別和friend函式一樣,給予protected, private權限給他。friend類別除了要在類別定義中放上friend類別原型外,如果夥伴類別和授與權限的類別是在不同的檔案中,夥伴類別須向前宣告(forward declaration),不需要#include。例如:stackNode和stack關係非常緊密,
範例:stackNode和stack
// stackNode.h
class Stack; // Forward declarartion
class StackNode {
friend class Stack; // Make stack a friend
public:
...
private:
int data;
StackNode *nextPtr;
};
// stack.h
#include "stackNode.h"
class Stack{
public:
...
private:
StackNode *firstPtr;
StackNode *lastPtr;
};
夥伴的授與是主動的,不能被動授與。因此如果stack想要拿到stackNode的權限,必須stackNode主動授與。要注意的是夥伴關係依舊不是類別的成員關係,因此夥伴函式/類別 (stack)需間接使用授與類別 (stackNode)的成員。也就是stack須先實例化stackNode,再透過stackNode物件去存取其中的protected, private成員。
(四)不夠朋友問題
在作業3當中,會遇到不夠朋友問題。以下程式碼為Complex類別中,宣告friend函式Polar(),可以看到參數列並沒有使用到Complex物件,這就會發生不夠朋友問題。
friend Complex Polar(const double leng, const double arg);
使用在類別中的friend函式時,如果friend函式的參數沒有使用類別本身,則main()就無法偵測到此friend函式,但如果friend函式參數有使用到類別本身,main()就可以偵測到此friend函式。其解決方法:
- 可以在.hpp的class外再宣告一次函式
- 或是在main.cpp中在宣告一次函式,這樣main()就可以被找到。
- 或是傳給Polar一個Complex的參數,這樣也可以。
這就像是我跟你是朋友,但你卻沒有使用到我(傳入的參數),我就不跟你好的概念,C++也是蠻講義氣的(X)。
更多此問題的討論
(五)實作細節
作業3實作上需要注意一些細節
1. 因為friend function不是member function,所以沒有this指標,故必須傳Complex& x給函數,因為x要更改,所以不能宣告成const
2. 因為+號已經overloading,所以直接使用x + y即可。不過因為x要更改,所以不能直接return x + y;,必須先更新x再回傳x。
// Overloading +=
Complex operator+=(Complex& x, const Complex& y){
x = x + y;
return x;
}
3. 如果不使用已經多載的+號,則必須先複製出一個xCopy,再更新x的值。不然直接更新x.real後,x.imag的更新會出問題。+=直接更新可能沒問題,但*=會出錯。
// Overloading +=
Complex operator+=(Complex& x, const Complex& y){
const Complex xCopy = x;
x.real = xCopy.real + y.real;
x.imag = xCopy.imag + y.imag;
return x;
}
更多作業細節,請參考AOOP Homework source code
留言
張貼留言