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

2021年7月21日 14:47
夥伴函式和成員函式差在哪裡?為什麼輸入/輸出的多載運算子一定要為夥伴函式?如果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; } 二、相關文章 4. [筆記]介面與實作、運算子多載、左值右值、參數傳遞、回傳多值|C++
運算子多載如何執行?int a = 10; ++a—;這串程式碼為什麼會出錯?居然跟左值又值有關!左值右值真的是一左一右嗎?參數傳遞中的傳指標和傳參考差在哪邊?C++居然無法回傳多值,那我該怎麼回傳多值? 5. [筆記]陣列與指標|C++
我以為我傳的是陣列,但居然傳的是指標?為什麼我無法回傳陣列?只能回傳指標?所以陣列和指標是什麼關係?指標運算怎麼算?釋放動態記憶體時,指標變數會不會也被釋放? 6. [筆記]類別、特殊函式、內嵌函式、函式物件|C++
程序導向的C語言也能實作物件導向?C語言要如何模擬類別?C struct與C++ struct與C++ class這三者有什麼差別?類別中有哪六個特殊函式?初始化和賦值有什麼差別?內嵌函式和巨集很像?物件居然可以拿來當成函式?為什麼C++宣告無參數物件時,不用加()? 7. [筆記]繼承模式與存取權限|C++
繼承模式和存取權限有什麼關係?private繼承不是讓所有存取權限變成private嗎?為什麼衍生類別依舊可以存取基礎類別?類別開發者和使用者有什麼區分?特殊函式的繼承會和一般函式一樣嗎? 8. [筆記]多型與繼承的關係|C++
多型的出現是為了要解決什麼問題?多型觸發的條件是什麼?為什麼多型要和繼承一起使用?多型能保有繼承的優點嗎?多型比繼承又多了什麼功能?特殊函式也能多型嗎? 9. [筆記]static / const成員資料與函式|C++
non-const static成員資料無法在類別內初始化,也無法使用初始化列,那到底該如何初始化?static成員是什麼概念?可以將自己宣告為自己的成員資料嗎?如果可以,該如何實現?如果不行,會發生什麼問題? 10. [筆記]夥伴函式與類別、不夠朋友問題|C++
夥伴函式和成員函式差在哪裡?為什麼輸入/輸出的多載運算子一定要為夥伴函式?如果A類別把B函式當作夥伴,但B函式的參數中沒有A類別,顯然這個B不把A當夥伴,這會造成main()無法找到B函式。
7
留言 0
文章資訊
85 篇文章294 人追蹤
Logo
每天有 7 則貼文