Series: C++
cpp
417 lines
· Updated 2026-04-03
friend_functions.cpp
C++/Part2_物件導向/Ch11_運算子多載/friend_functions.cpp
// ============================================================
// Ch11 — friend_functions.cpp
// friend 函式:Complex 類別、friend operator+、friend operator<<
// 何時使用 friend、friend class、非成員運算子多載
// 編譯:g++ -std=c++17 -Wall -o friend_functions friend_functions.cpp
// ============================================================
#include <iostream>
#include <cmath>
#include <string>
#include <vector>
// ────────────────────────────────────────────────────────────
// 1. Complex — 複數類別,展示 friend 函式
// ────────────────────────────────────────────────────────────
class Complex {
public:
Complex(double real = 0.0, double imag = 0.0)
: real_(real), imag_(imag) {}
// 成員函式版本的運算子(左運算元是 Complex)
Complex operator-() const {
return Complex(-real_, -imag_);
}
Complex& operator+=(const Complex& rhs) {
real_ += rhs.real_;
imag_ += rhs.imag_;
return *this;
}
Complex& operator-=(const Complex& rhs) {
real_ -= rhs.real_;
imag_ -= rhs.imag_;
return *this;
}
bool operator==(const Complex& rhs) const {
constexpr double eps = 1e-9;
return std::abs(real_ - rhs.real_) < eps &&
std::abs(imag_ - rhs.imag_) < eps;
}
bool operator!=(const Complex& rhs) const {
return !(*this == rhs);
}
double magnitude() const {
return std::sqrt(real_ * real_ + imag_ * imag_);
}
Complex conjugate() const {
return Complex(real_, -imag_);
}
// ── friend 函式宣告 ──
// 這些非成員函式可以存取 private 成員
// 算術運算子:使用 friend 讓 double + Complex 也能運作
friend Complex operator+(const Complex& a, const Complex& b);
friend Complex operator-(const Complex& a, const Complex& b);
friend Complex operator*(const Complex& a, const Complex& b);
friend Complex operator/(const Complex& a, const Complex& b);
// 串流運算子:左運算元是 ostream,必須用非成員
friend std::ostream& operator<<(std::ostream& os, const Complex& c);
friend std::istream& operator>>(std::istream& is, Complex& c);
// double 與 Complex 混合運算
friend Complex operator+(double lhs, const Complex& rhs);
friend Complex operator*(double lhs, const Complex& rhs);
private:
double real_;
double imag_;
};
// ── friend 函式實作 ──
Complex operator+(const Complex& a, const Complex& b) {
return Complex(a.real_ + b.real_, a.imag_ + b.imag_);
}
Complex operator-(const Complex& a, const Complex& b) {
return Complex(a.real_ - b.real_, a.imag_ - b.imag_);
}
Complex operator*(const Complex& a, const Complex& b) {
// (a+bi)(c+di) = (ac-bd) + (ad+bc)i
return Complex(a.real_ * b.real_ - a.imag_ * b.imag_,
a.real_ * b.imag_ + a.imag_ * b.real_);
}
Complex operator/(const Complex& a, const Complex& b) {
double denom = b.real_ * b.real_ + b.imag_ * b.imag_;
if (denom < 1e-15) {
throw std::runtime_error("除以零複數");
}
return Complex((a.real_ * b.real_ + a.imag_ * b.imag_) / denom,
(a.imag_ * b.real_ - a.real_ * b.imag_) / denom);
}
std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << c.real_;
if (c.imag_ >= 0) {
os << " + " << c.imag_ << "i";
} else {
os << " - " << (-c.imag_) << "i";
}
return os;
}
std::istream& operator>>(std::istream& is, Complex& c) {
// 輸入格式:real imag(以空白分隔)
is >> c.real_ >> c.imag_;
return is;
}
// double + Complex
Complex operator+(double lhs, const Complex& rhs) {
return Complex(lhs + rhs.real_, rhs.imag_);
}
// double * Complex
Complex operator*(double lhs, const Complex& rhs) {
return Complex(lhs * rhs.real_, lhs * rhs.imag_);
}
// ────────────────────────────────────────────────────────────
// 2. friend class 示範
// Engine 是 Car 的 friend class,可存取 Car 的 private 成員
// ────────────────────────────────────────────────────────────
class Car {
public:
Car(const std::string& model, int horsepower)
: model_(model), horsepower_(horsepower), running_(false) {}
void displayStatus() const {
std::cout << " [" << model_ << "] "
<< horsepower_ << " 馬力, "
<< (running_ ? "運轉中" : "已熄火") << "\n";
}
// Engine 是 friend class,可存取所有 private 成員
friend class Engine;
private:
std::string model_;
int horsepower_;
bool running_;
};
class Engine {
public:
// 因為是 friend class,可以直接操作 Car 的 private 成員
void startCar(Car& car) const {
if (!car.running_) {
car.running_ = true;
std::cout << " 引擎啟動 " << car.model_ << "\n";
} else {
std::cout << " " << car.model_ << " 已經在運轉了\n";
}
}
void stopCar(Car& car) const {
if (car.running_) {
car.running_ = false;
std::cout << " 引擎關閉 " << car.model_ << "\n";
} else {
std::cout << " " << car.model_ << " 已經熄火了\n";
}
}
void tuneUp(Car& car, int additionalHP) const {
car.horsepower_ += additionalHP;
std::cout << " " << car.model_ << " 調校完成,現在 "
<< car.horsepower_ << " 馬力\n";
}
};
// ────────────────────────────────────────────────────────────
// 3. 何時該用 friend,何時不該?— 對比示範
// ────────────────────────────────────────────────────────────
class Temperature {
public:
explicit Temperature(double celsius) : celsius_(celsius) {}
double celsius() const { return celsius_; }
double fahrenheit() const { return celsius_ * 9.0 / 5.0 + 32.0; }
// 方法一:透過 public getter,不需要 friend
// 但 operator<< 很常見,用 friend 更簡潔
friend std::ostream& operator<<(std::ostream& os, const Temperature& t);
// 比較運算子可以不用 friend(透過 getter 即可)
// 但寫成 friend 非成員可以支援隱式轉換
friend bool operator<(const Temperature& a, const Temperature& b);
friend bool operator>(const Temperature& a, const Temperature& b);
friend bool operator==(const Temperature& a, const Temperature& b);
private:
double celsius_;
};
std::ostream& operator<<(std::ostream& os, const Temperature& t) {
os << t.celsius_ << "°C (" << t.fahrenheit() << "°F)";
return os;
}
bool operator<(const Temperature& a, const Temperature& b) {
return a.celsius_ < b.celsius_;
}
bool operator>(const Temperature& a, const Temperature& b) {
return b < a;
}
bool operator==(const Temperature& a, const Temperature& b) {
constexpr double eps = 1e-9;
return std::abs(a.celsius_ - b.celsius_) < eps;
}
// ────────────────────────────────────────────────────────────
// 4. 非成員 + friend 配合 — 讓左右運算元都支援隱式轉換
// ────────────────────────────────────────────────────────────
class Money {
public:
Money(int dollars = 0, int cents = 0)
: total_cents_(dollars * 100 + cents) {}
int dollars() const { return total_cents_ / 100; }
int cents() const { return total_cents_ % 100; }
friend Money operator+(const Money& a, const Money& b);
friend Money operator-(const Money& a, const Money& b);
friend bool operator==(const Money& a, const Money& b);
friend bool operator<(const Money& a, const Money& b);
friend std::ostream& operator<<(std::ostream& os, const Money& m);
private:
int total_cents_;
};
Money operator+(const Money& a, const Money& b) {
Money result;
result.total_cents_ = a.total_cents_ + b.total_cents_;
return result;
}
Money operator-(const Money& a, const Money& b) {
Money result;
result.total_cents_ = a.total_cents_ - b.total_cents_;
return result;
}
bool operator==(const Money& a, const Money& b) {
return a.total_cents_ == b.total_cents_;
}
bool operator<(const Money& a, const Money& b) {
return a.total_cents_ < b.total_cents_;
}
std::ostream& operator<<(std::ostream& os, const Money& m) {
os << "$" << m.dollars() << "." ;
int c = m.cents();
if (c < 10) os << "0";
os << c;
return os;
}
// ============================================================
// main
// ============================================================
int main() {
std::cout << "========================================\n";
std::cout << " 1. Complex 複數運算(friend 運算子)\n";
std::cout << "========================================\n";
{
Complex a(3.0, 4.0);
Complex b(1.0, -2.0);
std::cout << " a = " << a << "\n";
std::cout << " b = " << b << "\n";
std::cout << " a + b = " << (a + b) << "\n";
std::cout << " a - b = " << (a - b) << "\n";
std::cout << " a * b = " << (a * b) << "\n";
std::cout << " a / b = " << (a / b) << "\n";
std::cout << " -a = " << (-a) << "\n";
std::cout << " |a| = " << a.magnitude() << "\n";
std::cout << " a 的共軛 = " << a.conjugate() << "\n";
}
std::cout << "\n========================================\n";
std::cout << " 2. double 與 Complex 混合運算\n";
std::cout << "========================================\n";
{
Complex c(2.0, 3.0);
// 2.0 + Complex — 需要 friend 非成員函式
Complex r1 = 5.0 + c;
std::cout << " 5.0 + " << c << " = " << r1 << "\n";
// 3.0 * Complex
Complex r2 = 3.0 * c;
std::cout << " 3.0 * " << c << " = " << r2 << "\n";
// Complex + Complex(也是 friend)
Complex r3 = c + Complex(1.0, 1.0);
std::cout << " " << c << " + (1 + 1i) = " << r3 << "\n";
}
std::cout << "\n========================================\n";
std::cout << " 3. Complex 比較與複合指派\n";
std::cout << "========================================\n";
{
Complex a(1.0, 2.0);
Complex b(1.0, 2.0);
Complex c(3.0, 4.0);
std::cout << " a = " << a << ", b = " << b << "\n";
std::cout << " a == b? " << (a == b ? "是" : "否") << "\n";
std::cout << " a != c? " << (a != c ? "是" : "否") << "\n";
a += c;
std::cout << " a += c → a = " << a << "\n";
a -= b;
std::cout << " a -= b → a = " << a << "\n";
}
std::cout << "\n========================================\n";
std::cout << " 4. friend class — Engine 操作 Car\n";
std::cout << "========================================\n";
{
Car myCar("Tesla Model 3", 283);
Engine engine;
myCar.displayStatus();
engine.startCar(myCar);
myCar.displayStatus();
engine.tuneUp(myCar, 50);
myCar.displayStatus();
engine.startCar(myCar); // 已經在運轉了
engine.stopCar(myCar);
myCar.displayStatus();
}
std::cout << "\n========================================\n";
std::cout << " 5. Temperature — friend 對比\n";
std::cout << "========================================\n";
{
Temperature boiling(100.0);
Temperature body(37.0);
Temperature freezing(0.0);
std::cout << " 沸點: " << boiling << "\n";
std::cout << " 體溫: " << body << "\n";
std::cout << " 冰點: " << freezing << "\n";
std::cout << " 體溫 < 沸點? " << (body < boiling ? "是" : "否") << "\n";
std::cout << " 沸點 > 冰點? " << (boiling > freezing ? "是" : "否") << "\n";
}
std::cout << "\n========================================\n";
std::cout << " 6. Money 類別 — friend 非成員運算\n";
std::cout << "========================================\n";
{
Money price(19, 99);
Money tax(1, 60);
Money discount(3, 0);
std::cout << " 價格: " << price << "\n";
std::cout << " 稅金: " << tax << "\n";
std::cout << " 折扣: " << discount << "\n";
Money total = price + tax - discount;
std::cout << " 總計: " << price << " + " << tax
<< " - " << discount << " = " << total << "\n";
std::cout << " price == Money(19,99)? "
<< (price == Money(19, 99) ? "是" : "否") << "\n";
std::cout << " tax < price? "
<< (tax < price ? "是" : "否") << "\n";
}
std::cout << "\n========================================\n";
std::cout << " 7. friend 使用原則總結\n";
std::cout << "========================================\n";
{
std::cout << R"(
┌──────────────────────────────────────────────────┐
│ friend 使用時機: │
│ ✓ operator<< / operator>> (左運算元是 stream) │
│ ✓ 左運算元不是自己的類別 (如 double + Complex) │
│ ✓ 兩個類別緊密耦合(如 Engine 與 Car) │
│ ✗ 可用 public getter 達成時,不需要 friend │
│ ✗ 過度使用 friend 會破壞封裝 │
└──────────────────────────────────────────────────┘
)";
}
std::cout << "========================================\n";
std::cout << " 程式結束\n";
std::cout << "========================================\n";
return 0;
}
Related articles
C++
c
Updated 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
Read article →
C++
c
Updated 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
Read article →
C++
cpp
Updated 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
Read article →
C++
c
Updated 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
Read article →
C++
c
Updated 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
Read article →
C++
cpp
Updated 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
Read article →