[筆記]夥伴函式與類別、不夠朋友問題|C++

夥伴函式和成員函式差在哪裡?為什麼輸入/輸出的多載運算子一定要為夥伴函式?如果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. 多載夥伴函式

  1. cost + tax
  2. cost.operator+(tax) //member function, cost must be an object,使用上比較沒有彈性
  3. 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函式。其解決方法:

  1. 可以在.hpp的class外再宣告一次函式
  2. 或是在main.cpp中在宣告一次函式,這樣main()就可以被找到。
  3. 或是傳給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

二、相關文章

留言