S SmartDocs
Série: C++ cpp 417 lignes · Mis à jour 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;
}

Articles liés