《C++ Primer》第5版 友元 (friend)

C++ 教材(《C++ Primer》第5版)章节标题为:

7.2.1 友元 (friend)

本节核心内容是:当类的数据成员设为 private 时,如何让非成员函数(如 read, print, add)能够访问这些私有成员?答案是——使用 friend 关键字声明“友元函数”。

这是面向对象设计中“封装性”与“接口灵活性”之间的重要平衡机制。


🔍 逐段解析


✅ 第一段:问题背景

既然 Sales_data 的数据成员是 private 的,我们的 read、print 和 add 函数也就无法正常编译了,这是因为尽管这几个函数是类的接口的一部分,但它们不是类的成员。

💡 核心要点:
  • 如果将 bookNo, units_sold, revenue 设为 private(推荐做法),那么外部函数(包括非成员函数)默认无法访问
  • read, print, add 在逻辑上属于类的接口,需要访问这些数据。
  • 矛盾:封装性 vs 接口需求
🧩 解决方案:

类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元(friend)。

—— 这就是 friend 关键字的作用:打破封装限制,授予特定函数/类访问权限。


✅ 第二段:语法说明

如果类想把一个函数作为它的友元,只需要增加一条以 friend 关键字开始的函数声明语句即可:

class Sales_data {
    // 为 Sales_data 的非成员函数所做的友元声明
    friend Sales_data add(const Sales_data&, const Sales_data&);
    friend std::istream &read(std::istream&, Sales_data&);
    friend std::ostream &print(std::ostream&, const Sales_data&);
    // 其他成员及访问说明符与之前一致
public:
    // ... 构造函数和公有成员函数
private:
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
// Sales_data 接口的非成员组成部分的声明
Sales_data add(const Sales_data&, const Sales_data&);
💡 关键语法点:
  1. friend 声明位置

    • 可以在 publicprotectedprivate 区域声明,不影响访问权限
    • 通常放在类定义开头或结尾,便于阅读。
  2. friend 声明形式

    • 就像普通函数声明,前面加 friend 关键字。
    • 不需要函数体(只是声明)。
    • 参数类型必须与实际定义完全匹配。
  3. 友元关系是单向的

    • Sales_data 声明 read 是友元 → read 可以访问 Sales_data 的私有成员。
    • read 不能反过来让 Sales_data 访问自己的私有成员(除非也声明)。
  4. 友元不传递、不继承

    • A 是 B 的友元,B 是 C 的友元 ≠ A 是 C 的友元。
    • 派生类不会自动继承基类的友元关系。
  5. 友元声明 ≠ 定义

    • 类内的 friend 只是声明,函数定义仍需在类外单独写出。
    • 注意最后一行:
      Sales_data add(const Sales_data&, const Sales_data&);
      
      —— 这是在类外再次声明该函数(因为它是非成员函数),确保链接器能找到它。

🎯 为什么需要友元?

场景为什么不用成员函数?为什么用友元?
输入输出操作(read, print)第一个参数是流对象 (istream/ostream),不是 Sales_data 本身需要访问私有成员,但不适合做成成员函数
对称二元运算(add, operator+)两个参数地位平等,若做成成员函数,左边必须是 Sales_data 对象友元函数可自由安排参数顺序,更自然
工具函数(如比较、转换等)不属于类的核心行为,不应污染类接口保持类简洁,同时提供必要功能

⚠️ 易错点提醒

错误1:忘记在类外重新声明非成员函数

class Sales_data {
    friend Sales_data add(const Sales_data&, const Sales_data&);
    // ...
};

// ❌ 错误:缺少类外声明!编译器可能找不到这个函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
    // ...
}

✅ 正确做法:

class Sales_data {
    friend Sales_data add(const Sales_data&, const Sales_data&);
    // ...
};

// ✅ 必须在类外再次声明(尤其在头文件中)
Sales_data add(const Sales_data&, const Sales_data&);

// 然后在 .cpp 文件中定义
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
    // ...
}

📌 注意:有些编译器允许省略类外声明,但为了可移植性和清晰性,强烈建议显式声明


错误2:误以为友元是双向的

class A {
    friend void func(A&, B&);  // func 可以访问 A 的私有成员
};

class B {
    // ❌ func 不能访问 B 的私有成员,除非 B 也声明 func 为友元
};

✅ 正确做法:

class A {
    friend void func(A&, B&);
};

class B {
    friend void func(A&, B&);  // 必须也声明
};

错误3:在友元声明中使用错误的参数类型

class Sales_data {
    friend std::ostream& print(std::ostream&, Sales_data&);  // ❌ 应该是 const 引用
};

✅ 正确做法:

friend std::ostream& print(std::ostream&, const Sales_data&);  // ✅ 只读,安全高效

💡 实际应用示例

假设你有一个 Matrix 类,想重载 operator<< 输出矩阵:

class Matrix {
    friend std::ostream& operator<<(std::ostream&, const Matrix&);
private:
    std::vector<std::vector<int>> data;
};

std::ostream& operator<<(std::ostream& os, const Matrix& m) {
    for (const auto& row : m.data) {  // 访问私有成员 data
        for (int val : row) {
            os << val << " ";
        }
        os << "\n";
    }
    return os;
}

没有 friend,这段代码会编译失败!


📘 教学意义

本节内容是理解 C++ “封装”与“开放”平衡的关键:

  • 封装性:通过 private 保护内部实现。
  • 灵活性:通过 friend 有选择地开放接口。
  • 设计哲学:不是所有相关操作都要写成成员函数;合理使用权责分离。

这也是后续学习“友元类”、“运算符重载”、“迭代器模式”等高级主题的基础。


✅ 总结对比表

特性成员函数非成员函数 + 友元
访问私有成员✅ 自动拥有✅ 需声明为 friend
调用方式obj.func()func(obj)
对称性左边必须是对象参数可自由安排
封装性较弱(需谨慎使用)
适用场景核心行为、状态修改IO、算术运算、工具函数

🎓 最终结论

“友元”是 C++ 中一种强大而危险的工具:

  • 强大:它让你能在保持封装的同时,灵活扩展类的能力。
  • 危险:滥用会破坏封装,导致代码难以维护。

最佳实践

  • 仅在必要时使用友元(如 IO、对称运算)。
  • 尽量优先通过公有接口完成任务。
  • 明确文档化每个友元的用途。
  • 避免过度使用,尤其是跨多个类的复杂友元网络。

掌握友元机制,标志着你对 C++ 面向对象设计的理解进入了更深层次!

🎯 友元函数的具体应用场景

友元函数是 C++ 中一个特殊而强大的机制。下面我为您详细梳理友元函数的7大核心应用场景,每个场景都配有完整的代码示例。


📋 应用场景总览

场景使用频率必要性典型例子
1️⃣ 输入输出运算符重载⭐⭐⭐⭐⭐必须operator<<, operator>>
2️⃣ 对称二元运算符重载⭐⭐⭐⭐⭐必须operator+, operator==
3️⃣ 访问多个类私有成员⭐⭐⭐⭐必须跨类操作函数
4️⃣ 工厂函数⭐⭐⭐推荐创建对象的辅助函数
5️⃣ 工具/辅助函数⭐⭐⭐推荐数学计算、转换等
6️⃣ 迭代器模式⭐⭐⭐推荐容器迭代器
7️⃣ 单例模式⭐⭐推荐私有构造函数的类

1️⃣ 输入输出运算符重载(最经典场景)

🎯 为什么必须用友元?

  • operator<<左操作数是 ostream,不是我们的类
  • 无法将 operator<< 定义为成员函数(成员函数的 this 指向左操作数)
  • 需要访问类的私有成员来输出

✅ 代码示例

#include <iostream>
#include <string>

class Person {
    // 声明友元函数
    friend std::ostream& operator<<(std::ostream&, const Person&);
    friend std::istream& operator>>(std::istream&, Person&);
    
private:
    std::string name;
    int age;
    std::string email;  // 私有数据
    
public:
    Person() = default;
    Person(const std::string& n, int a, const std::string& e)
        : name(n), age(a), email(e) {}
};

// 输出运算符重载(友元函数)
std::ostream& operator<<(std::ostream& os, const Person& p) {
    os << "Name: " << p.name      // ✅ 可以访问私有成员
       << ", Age: " << p.age
       << ", Email: " << p.email;
    return os;
}

// 输入运算符重载(友元函数)
std::istream& operator>>(std::istream& is, Person& p) {
    is >> p.name >> p.age >> p.email;  // ✅ 可以访问私有成员
    return is;
}

// 使用
int main() {
    Person p("Alice", 25, "alice@example.com");
    
    std::cout << p << std::endl;  // 链式输出
    
    Person p2;
    std::cin >> p2;  // 链式输入
    std::cout << p2 << std::endl;
    
    return 0;
}

📊 对比:成员函数 vs 友元函数

方式代码问题
❌ 成员函数p.operator<<(cout)语法反直觉,左操作数必须是对象
✅ 友元函数cout << p语法自然,符合习惯

2️⃣ 对称二元运算符重载

🎯 为什么必须用友元?

当运算符的两个操作数地位平等时(如 +, -, ==),如果定义为成员函数,左操作数必须是类对象,这会导致不对称:

// 成员函数版本
Complex c1, c2;
c1 + c2;        // ✅ 可以:c1.operator+(c2)
5 + c2;         // ❌ 错误:5 没有 operator+ 成员函数

✅ 代码示例

#include <iostream>

class Complex {
    friend Complex operator+(const Complex&, const Complex&);
    friend Complex operator+(const Complex&, double);
    friend Complex operator+(double, const Complex&);  // ✅ 关键!
    
private:
    double real;
    double imag;
    
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
    void print() const {
        std::cout << real << " + " << imag << "i" << std::endl;
    }
};

// 两个 Complex 相加
Complex operator+(const Complex& c1, const Complex& c2) {
    return Complex(c1.real + c2.real, c1.imag + c2.imag);
}

// Complex + double
Complex operator+(const Complex& c, double d) {
    return Complex(c.real + d, c.imag);
}

// double + Complex(必须是友元!)
Complex operator+(double d, const Complex& c) {
    return Complex(c.real + d, c.imag);
}

// 使用
int main() {
    Complex c1(1, 2);
    Complex c2(3, 4);
    
    c1 + c2;        // ✅ 两边都是 Complex
    c1 + 5.0;       // ✅ Complex + double
    5.0 + c1;       // ✅ double + Complex(成员函数无法实现!)
    
    c1.print();
    return 0;
}

📊 对称性对比

运算符成员函数友元函数
c1 + c2
c1 + 5.0
5.0 + c1

3️⃣ 访问多个类私有成员的函数

🎯 为什么必须用友元?

当一个函数需要同时访问两个或多个类的私有成员时,必须将这些类都声明该函数为友元。

✅ 代码示例

#include <iostream>
#include <string>

class Date;  // 前向声明

class Person {
    friend void compareAges(const Person&, const Person&);
    friend void printPersonDate(const Person&, const Date&);
    
private:
    std::string name;
    int age;
    
public:
    Person(const std::string& n, int a) : name(n), age(a) {}
    std::string getName() const { return name; }
};

class Date {
    friend void printPersonDate(const Person&, const Date&);
    
private:
    int year, month, day;
    
public:
    Date(int y, int m, int d) : year(y), month(m), day(d) {}
};

// 需要访问两个类的私有成员
void printPersonDate(const Person& p, const Date& d) {
    std::cout << p.name << " was born on "  // ✅ 访问 Person 私有成员
              << d.year << "-" << d.month << "-" << d.day  // ✅ 访问 Date 私有成员
              << std::endl;
}

// 比较两个 Person 的年龄
void compareAges(const Person& p1, const Person& p2) {
    if (p1.age > p2.age)  // ✅ 访问私有成员
        std::cout << p1.name << " is older" << std::endl;
    else if (p1.age < p2.age)
        std::cout << p2.name << " is older" << std::endl;
    else
        std::cout << "Same age" << std::endl;
}

// 使用
int main() {
    Person p1("Alice", 25);
    Person p2("Bob", 30);
    Date d(1998, 5, 15);
    
    compareAges(p1, p2);
    printPersonDate(p1, d);
    
    return 0;
}

4️⃣ 工厂函数(Factory Function)

🎯 为什么用友元?

当类有私有构造函数(如单例模式、对象池),但需要特定函数来创建对象时。

✅ 代码示例

#include <iostream>
#include <memory>
#include <vector>

class Database {
    // 声明工厂函数为友元
    friend std::unique_ptr<Database> createDatabase(const std::string& connStr);
    friend class DatabasePool;  // 友元类
    
private:
    std::string connectionString;
    bool connected;
    
    // 私有构造函数:防止外部直接创建
    Database(const std::string& conn) 
        : connectionString(conn), connected(false) {
        std::cout << "Database created (private constructor)" << std::endl;
    }
    
public:
    void connect() {
        connected = true;
        std::cout << "Connected to " << connectionString << std::endl;
    }
    
    // 禁止拷贝
    Database(const Database&) = delete;
    Database& operator=(const Database&) = delete;
};

// 工厂函数:可以调用私有构造函数
std::unique_ptr<Database> createDatabase(const std::string& connStr) {
    return std::make_unique<Database>(connStr);  // ✅ 可以访问私有构造函数
}

// 对象池类(友元类)
class DatabasePool {
public:
    std::vector<std::unique_ptr<Database>> pools;
    
    void addDatabase(const std::string& connStr) {
        pools.push_back(std::make_unique<Database>(connStr));  // ✅ 可以访问私有构造函数
    }
};

// 使用
int main() {
    // ❌ 错误:不能直接创建
    // Database db("mysql://localhost");
    
    // ✅ 正确:通过工厂函数创建
    auto db = createDatabase("mysql://localhost");
    db->connect();
    
    // ✅ 正确:通过对象池创建
    DatabasePool pool;
    pool.addDatabase("mysql://db1");
    pool.addDatabase("mysql://db2");
    
    return 0;
}

5️⃣ 工具/辅助函数

🎯 为什么用友元?

当需要一个独立于类的工具函数,但又需要访问类的内部实现时。

✅ 代码示例

#include <iostream>
#include <cmath>

class Vector3D {
    friend double dotProduct(const Vector3D&, const Vector3D&);
    friend Vector3D crossProduct(const Vector3D&, const Vector3D&);
    friend double distance(const Vector3D&, const Vector3D&);
    
private:
    double x, y, z;
    
public:
    Vector3D(double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z) {}
    
    double magnitude() const {
        return std::sqrt(x*x + y*y + z*z);
    }
};

// 点积(需要访问两个向量的私有成员)
double dotProduct(const Vector3D& v1, const Vector3D& v2) {
    return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}

// 叉积(返回新向量)
Vector3D crossProduct(const Vector3D& v1, const Vector3D& v2) {
    return Vector3D(
        v1.y * v2.z - v1.z * v2.y,
        v1.z * v2.x - v1.x * v2.z,
        v1.x * v2.y - v1.y * v2.x
    );
}

// 计算两点距离
double distance(const Vector3D& v1, const Vector3D& v2) {
    double dx = v1.x - v2.x;
    double dy = v1.y - v2.y;
    double dz = v1.z - v2.z;
    return std::sqrt(dx*dx + dy*dy + dz*dz);
}

// 使用
int main() {
    Vector3D v1(1, 2, 3);
    Vector3D v2(4, 5, 6);
    
    std::cout << "Dot product: " << dotProduct(v1, v2) << std::endl;
    std::cout << "Distance: " << distance(v1, v2) << std::endl;
    
    Vector3D v3 = crossProduct(v1, v2);
    std::cout << "Cross product magnitude: " << v3.magnitude() << std::endl;
    
    return 0;
}

6️⃣ 迭代器模式

🎯 为什么用友元?

迭代器需要访问容器的内部数据结构(如链表节点、数组指针),但不应该是容器的成员。

✅ 代码示例

#include <iostream>

class LinkedList;  // 前向声明

class Iterator {
    friend class LinkedList;  // LinkedList 是 Iterator 的友元
    
private:
    struct Node {
        int data;
        Node* next;
        Node(int d) : data(d), next(nullptr) {}
    };
    
    Node* current;
    
    // 私有构造函数:只能通过 LinkedList 创建
    Iterator(Node* node) : current(node) {}
    
public:
    int& operator*() { return current->data; }
    
    Iterator& operator++() {
        current = current->next;
        return *this;
    }
    
    bool operator!=(const Iterator& other) const {
        return current != other.current;
    }
};

class LinkedList {
    friend class Iterator;  // Iterator 是 LinkedList 的友元
    
private:
    struct Node {
        int data;
        Node* next;
        Node(int d) : data(d), next(nullptr) {}
    };
    
    Node* head;
    
public:
    LinkedList() : head(nullptr) {}
    
    void pushFront(int value) {
        Node* newNode = new Node(value);
        newNode->next = head;
        head = newNode;
    }
    
    // 返回迭代器(可以访问私有 Node 结构)
    Iterator begin() {
        return Iterator(head);  // ✅ 可以访问 Iterator 的私有构造函数
    }
    
    Iterator end() {
        return Iterator(nullptr);
    }
};

// 使用
int main() {
    LinkedList list;
    list.pushFront(3);
    list.pushFront(2);
    list.pushFront(1);
    
    for (Iterator it = list.begin(); it != list.end(); ++it) {
        std::cout << *it << " ";  // 输出: 1 2 3
    }
    std::cout << std::endl;
    
    return 0;
}

7️⃣ 单例模式(Singleton)

🎯 为什么用友元?

单例模式需要私有构造函数防止外部创建,但需要特定方法获取唯一实例。

✅ 代码示例

#include <iostream>

class Singleton {
    // 声明获取实例的函数为友元(可选,通常用静态成员函数)
    friend Singleton& getInstance();
    
private:
    static Singleton* instance;
    int data;
    
    // 私有构造函数
    Singleton() : data(0) {
        std::cout << "Singleton created" << std::endl;
    }
    
    // 禁止拷贝和赋值
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
public:
    void setData(int d) { data = d; }
    int getData() const { return data; }
    
    // 析构函数
    ~Singleton() {
        std::cout << "Singleton destroyed" << std::endl;
    }
};

// 静态成员初始化
Singleton* Singleton::instance = nullptr;

// 友元函数:获取唯一实例
Singleton& getInstance() {
    if (Singleton::instance == nullptr) {
        Singleton::instance = new Singleton();  // ✅ 可以调用私有构造函数
    }
    return *Singleton::instance;
}

// 使用
int main() {
    // ❌ 错误:不能直接创建
    // Singleton s;
    
    // ✅ 正确:通过友元函数获取
    Singleton& s1 = getInstance();
    s1.setData(42);
    
    Singleton& s2 = getInstance();  // 返回同一个实例
    std::cout << "Data: " << s2.getData() << std::endl;
    
    std::cout << "Same instance: " << (&s1 == &s2) << std::endl;
    
    return 0;
}

⚠️ 友元使用的注意事项

1. 友元破坏封装性

// ❌ 滥用友元:所有函数都是友元
class MyClass {
    friend void func1(MyClass&);
    friend void func2(MyClass&);
    friend void func3(MyClass&);
    // ... 太多友元 = 没有封装
private:
    int data;
};

// ✅ 合理使用:仅必要的函数是友元
class MyClass {
    friend std::ostream& operator<<(std::ostream&, const MyClass&);
private:
    int data;
public:
    int getData() const { return data; }  // 通过公有接口访问
};

2. 友元不传递

class A {
    friend void func(A&);
private:
    int x;
};

class B {
    // func 不是 B 的友元,即使 func 是 A 的友元
private:
    int y;
};

void func(A& a) {
    a.x = 10;  // ✅ 可以
    // b.y = 20;  // ❌ 错误:func 不是 B 的友元
}

3. 友元不继承

class Base {
    friend void func(Base&);
protected:
    int x;
};

class Derived : public Base {
    // func 不是 Derived 的友元!
private:
    int y;
};

void func(Base& b) {
    b.x = 10;  // ✅ 可以访问 Base 的 protected 成员
    // d.y = 20;  // ❌ 错误:不能访问 Derived 的私有成员
}

📊 决策树:何时使用友元?

需要访问类的私有成员?
│
├─ 是 → 该函数应该是成员函数吗?
│       │
│       ├─ 是 → 定义为成员函数 ✅
│       │
│       └─ 否 → 需要访问多个类的私有成员吗?
│               │
│               ├─ 是 → 使用友元函数 ✅
│               │
│               └─ 否 → 左操作数必须是其他类型吗?(如 ostream)
│                       │
│                       ├─ 是 → 使用友元函数 ✅
│                       │
│                       └─ 否 → 考虑通过公有接口访问 ✅
│
└─ 否 → 不需要友元 ✅

✅ 最佳实践总结

原则说明
最小化原则只声明必要的友元,越少越好
文档化注释说明为什么需要这个友元
优先公有接口能通过公有成员函数解决的,不用友元
避免友元类友元函数比友元类更安全(权限更小)
谨慎跨模块不同模块/库之间尽量避免友元

🎓 总结

友元函数是 C++ 中必要但需谨慎使用的机制:

场景必要性推荐度
输入输出运算符必须⭐⭐⭐⭐⭐
对称运算符必须⭐⭐⭐⭐⭐
跨类操作必须⭐⭐⭐⭐
工厂函数推荐⭐⭐⭐
工具函数可选⭐⭐
迭代器推荐⭐⭐⭐
单例模式可选⭐⭐

记住:友元是打破封装的"特权",应该像使用特权一样谨慎!

📘 友元函数完整指南:10+ 实际应用场景详解

我将为您提供一份超详细的友元函数应用指南,包含 10+ 个实际应用场景,每个都有完整的可运行代码示例。


📋 目录

场景难度实用性章节
1. 输入输出运算符重载⭐⭐⭐⭐⭐点击查看
2. 对称二元运算符⭐⭐⭐⭐⭐⭐⭐点击查看
3. 跨类私有成员访问⭐⭐⭐⭐⭐⭐⭐点击查看
4. 工厂模式⭐⭐⭐⭐⭐⭐⭐点击查看
5. 单例模式⭐⭐⭐⭐⭐⭐点击查看
6. 迭代器模式⭐⭐⭐⭐⭐⭐⭐⭐⭐点击查看
7. 矩阵/向量运算⭐⭐⭐⭐⭐⭐⭐点击查看
8. 序列化/反序列化⭐⭐⭐⭐⭐⭐⭐⭐点击查看
9. 测试框架⭐⭐⭐⭐⭐⭐⭐点击查看
10. 缓存系统⭐⭐⭐⭐⭐⭐⭐⭐点击查看
11. 观察者模式⭐⭐⭐⭐⭐⭐⭐⭐点击查看
12. 智能指针⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐点击查看

场景1:输入输出运算符重载

🎯 为什么必须用友元?

operator<< 的左操作数是 ostream,不是我们的类,无法定义为成员函数。

✅ 完整代码示例

// File: student.h
#ifndef STUDENT_H
#define STUDENT_H

#include <iostream>
#include <string>
#include <vector>

class Student {
    // 友元声明:输入输出运算符
    friend std::ostream& operator<<(std::ostream&, const Student&);
    friend std::istream& operator>>(std::istream&, Student&);
    
private:
    std::string name;
    int id;
    double gpa;
    std::vector<std::string> courses;
    
public:
    Student() : id(0), gpa(0.0) {}
    Student(const std::string& n, int i, double g) 
        : name(n), id(i), gpa(g) {}
    
    void addCourse(const std::string& course) {
        courses.push_back(course);
    }
    
    std::string getName() const { return name; }
    int getId() const { return id; }
    double getGPA() const { return gpa; }
};

// 输出运算符重载
std::ostream& operator<<(std::ostream& os, const Student& s) {
    os << "Student[ID: " << s.id << ", Name: " << s.name 
       << ", GPA: " << s.gpa << ", Courses: ";
    for (size_t i = 0; i < s.courses.size(); ++i) {
        if (i > 0) os << ", ";
        os << s.courses[i];
    }
    os << "]";
    return os;
}

// 输入运算符重载
std::istream& operator>>(std::istream& is, Student& s) {
    std::cout << "Enter name: ";
    is >> s.name;
    std::cout << "Enter ID: ";
    is >> s.id;
    std::cout << "Enter GPA: ";
    is >> s.gpa;
    return is;
}

#endif
// File: main.cpp
#include "student.h"

int main() {
    Student s1("Alice", 1001, 3.8);
    s1.addCourse("Math");
    s1.addCourse("Physics");
    
    // 链式输出
    std::cout << s1 << std::endl;
    
    // 链式输入
    Student s2;
    std::cin >> s2;
    std::cout << s2 << std::endl;
    
    // 输出到文件
    #include <fstream>
    std::ofstream file("student.txt");
    file << s1 << std::endl;
    file.close();
    
    return 0;
}

📊 对比:成员函数 vs 友元函数

特性成员函数(不可行)友元函数(推荐)
语法s.operator<<(cout)cout << s
链式调用❌ 困难✅ 自然
左操作数必须是 Student可以是 ostream
符合习惯❌ 反直觉✅ 符合直觉

场景2:对称二元运算符

🎯 为什么必须用友元?

确保 a + bb + a 都能工作,即使其中一个不是类对象。

✅ 完整代码示例

// File: rational.h
#ifndef RATIONAL_H
#define RATIONAL_H

#include <iostream>
#include <numeric>

class Rational {
    friend Rational operator+(const Rational&, const Rational&);
    friend Rational operator+(const Rational&, int);
    friend Rational operator+(int, const Rational&);  // 关键!
    
    friend Rational operator*(const Rational&, const Rational&);
    friend Rational operator*(const Rational&, int);
    friend Rational operator*(int, const Rational&);
    
    friend bool operator==(const Rational&, const Rational&);
    friend bool operator==(const Rational&, int);
    friend bool operator==(int, const Rational&);
    
    friend std::ostream& operator<<(std::ostream&, const Rational&);
    
private:
    int numerator;
    int denominator;
    
    void simplify() {
        int gcd = std::gcd(numerator, denominator);
        numerator /= gcd;
        denominator /= gcd;
        if (denominator < 0) {
            numerator = -numerator;
            denominator = -denominator;
        }
    }
    
public:
    Rational(int n = 0, int d = 1) : numerator(n), denominator(d) {
        simplify();
    }
    
    double toDouble() const {
        return static_cast<double>(numerator) / denominator;
    }
};

// Rational + Rational
Rational operator+(const Rational& r1, const Rational& r2) {
    int n = r1.numerator * r2.denominator + r2.numerator * r1.denominator;
    int d = r1.denominator * r2.denominator;
    return Rational(n, d);
}

// Rational + int
Rational operator+(const Rational& r, int i) {
    return r + Rational(i, 1);
}

// int + Rational(必须是友元!)
Rational operator+(int i, const Rational& r) {
    return Rational(i, 1) + r;
}

// Rational * Rational
Rational operator*(const Rational& r1, const Rational& r2) {
    return Rational(r1.numerator * r2.numerator, 
                    r1.denominator * r2.denominator);
}

// Rational * int
Rational operator*(const Rational& r, int i) {
    return r * Rational(i, 1);
}

// int * Rational
Rational operator*(int i, const Rational& r) {
    return Rational(i, 1) * r;
}

// 比较运算符
bool operator==(const Rational& r1, const Rational& r2) {
    return r1.numerator == r2.numerator && 
           r1.denominator == r2.denominator;
}

bool operator==(const Rational& r, int i) {
    return r == Rational(i, 1);
}

bool operator==(int i, const Rational& r) {
    return Rational(i, 1) == r;
}

// 输出
std::ostream& operator<<(std::ostream& os, const Rational& r) {
    if (r.denominator == 1)
        os << r.numerator;
    else
        os << r.numerator << "/" << r.denominator;
    return os;
}

#endif
// File: main.cpp
#include "rational.h"

int main() {
    Rational r1(1, 2);  // 1/2
    Rational r2(1, 3);  // 1/3
    
    // 对称性测试
    std::cout << r1 << " + " << r2 << " = " << (r1 + r2) << std::endl;
    std::cout << r1 << " + " << 5 << " = " << (r1 + 5) << std::endl;
    std::cout << 5 << " + " << r1 << " = " << (5 + r1) << std::endl;  // 关键!
    
    std::cout << r1 << " * " << r2 << " = " << (r1 * r2) << std::endl;
    std::cout << r1 << " * " << 3 << " = " << (r1 * 3) << std::endl;
    std::cout << 3 << " * " << r1 << " = " << (3 * r1) << std::endl;  // 关键!
    
    // 比较
    std::cout << (r1 == Rational(1, 2)) << std::endl;  // 1
    std::cout << (r1 == 0) << std::endl;  // 0
    std::cout << (1 == Rational(1, 1)) << std::endl;  // 1
    
    return 0;
}

📊 对称性测试结果

1/2 + 1/3 = 5/6
1/2 + 5 = 11/2
5 + 1/2 = 11/2          ← 成员函数无法实现!
1/2 * 1/3 = 1/6
1/2 * 3 = 3/2
3 * 1/2 = 3/2           ← 成员函数无法实现!

场景3:跨类私有成员访问

🎯 为什么必须用友元?

一个函数需要同时访问两个不同类的私有成员。

✅ 完整代码示例

// File: geometry.h
#ifndef GEOMETRY_H
#define GEOMETRY_H

#include <iostream>
#include <cmath>
#include <string>

class Point;
class Rectangle;

// 跨类操作函数声明
double distance(const Point&, const Point&);
bool contains(const Rectangle&, const Point&);
void printRelation(const Rectangle&, const Point&);

class Point {
    friend double distance(const Point&, const Point&);
    friend bool contains(const Rectangle&, const Point&);
    friend void printRelation(const Rectangle&, const Point&);
    
private:
    double x, y;
    
public:
    Point(double x = 0, double y = 0) : x(x), y(y) {}
    
    std::string toString() const {
        return "(" + std::to_string(x) + ", " + std::to_string(y) + ")";
    }
};

class Rectangle {
    friend bool contains(const Rectangle&, const Point&);
    friend void printRelation(const Rectangle&, const Point&);
    
private:
    Point topLeft;
    Point bottomRight;
    
public:
    Rectangle(double x1, double y1, double x2, double y2)
        : topLeft(x1, y1), bottomRight(x2, y2) {}
    
    double getWidth() const {
        return bottomRight.x - topLeft.x;
    }
    
    double getHeight() const {
        return topLeft.y - bottomRight.y;
    }
    
    double getArea() const {
        return getWidth() * getHeight();
    }
};

// 计算两点距离(访问两个 Point 的私有成员)
double distance(const Point& p1, const Point& p2) {
    double dx = p1.x - p2.x;  // ✅ 访问私有成员
    double dy = p1.y - p2.y;  // ✅ 访问私有成员
    return std::sqrt(dx * dx + dy * dy);
}

// 判断点是否在矩形内(访问 Rectangle 和 Point 的私有成员)
bool contains(const Rectangle& rect, const Point& p) {
    return p.x >= rect.topLeft.x && p.x <= rect.bottomRight.x &&
           p.y <= rect.topLeft.y && p.y >= rect.bottomRight.y;
}

// 打印关系信息
void printRelation(const Rectangle& rect, const Point& p) {
    std::cout << "Rectangle area: " << rect.getArea() << std::endl;
    std::cout << "Point: " << p.toString() << std::endl;
    
    if (contains(rect, p)) {
        std::cout << "Point is INSIDE the rectangle" << std::endl;
    } else {
        std::cout << "Point is OUTSIDE the rectangle" << std::endl;
    }
    
    double distToTopLeft = distance(p, rect.topLeft);
    std::cout << "Distance to top-left: " << distToTopLeft << std::endl;
}

#endif
// File: main.cpp
#include "geometry.h"

int main() {
    Point p1(0, 0);
    Point p2(3, 4);
    
    std::cout << "Distance between " << p1.toString() 
              << " and " << p2.toString() 
              << " = " << distance(p1, p2) << std::endl;
    
    Rectangle rect(0, 10, 10, 0);
    Point p3(5, 5);
    Point p4(15, 15);
    
    std::cout << "\n--- Rectangle Test ---" << std::endl;
    printRelation(rect, p3);
    std::cout << std::endl;
    printRelation(rect, p4);
    
    return 0;
}

📊 输出结果

Distance between (0.000000, 0.000000) and (3.000000, 4.000000) = 5

--- Rectangle Test ---
Rectangle area: 100
Point: (5.000000, 5.000000)
Point is INSIDE the rectangle
Distance to top-left: 7.07107

Rectangle area: 100
Point: (15.000000, 15.000000)
Point is OUTSIDE the rectangle
Distance to top-left: 18.0278

场景4:工厂模式

🎯 为什么用友元?

控制对象创建,确保所有对象都通过工厂函数创建(便于资源管理、日志记录等)。

✅ 完整代码示例

// File: vehicle.h
#ifndef VEHICLE_H
#define VEHICLE_H

#include <iostream>
#include <string>
#include <memory>
#include <vector>

class VehicleFactory;

class Vehicle {
    friend class VehicleFactory;
    
private:
    std::string type;
    std::string model;
    int year;
    double price;
    
    // 私有构造函数
    Vehicle(const std::string& t, const std::string& m, int y, double p)
        : type(t), model(m), year(y), price(p) {
        std::cout << "[LOG] Vehicle created: " << model << std::endl;
    }
    
public:
    // 禁止拷贝
    Vehicle(const Vehicle&) = delete;
    Vehicle& operator=(const Vehicle&) = delete;
    
    // 允许移动
    Vehicle(Vehicle&&) = default;
    Vehicle& operator=(Vehicle&&) = default;
    
    std::string getInfo() const {
        return type + " - " + model + " (" + std::to_string(year) + ") - $" 
               + std::to_string(static_cast<int>(price));
    }
    
    ~Vehicle() {
        std::cout << "[LOG] Vehicle destroyed: " << model << std::endl;
    }
};

// 工厂类
class VehicleFactory {
private:
    static std::vector<std::unique_ptr<Vehicle>> inventory;
    
public:
    // 创建车辆(可以调用私有构造函数)
    static std::unique_ptr<Vehicle> createCar(const std::string& model, 
                                               int year, double price) {
        auto v = std::make_unique<Vehicle>("Car", model, year, price);
        inventory.push_back(std::make_unique<Vehicle>(*v));
        return v;
    }
    
    static std::unique_ptr<Vehicle> createTruck(const std::string& model, 
                                                 int year, double price) {
        auto v = std::make_unique<Vehicle>("Truck", model, year, price);
        inventory.push_back(std::make_unique<Vehicle>(*v));
        return v;
    }
    
    static size_t getInventorySize() {
        return inventory.size();
    }
    
    static void printInventory() {
        std::cout << "\n=== Vehicle Inventory ===" << std::endl;
        for (const auto& v : inventory) {
            std::cout << v->getInfo() << std::endl;
        }
        std::cout << "Total: " << inventory.size() << " vehicles" << std::endl;
    }
};

std::vector<std::unique_ptr<Vehicle>> VehicleFactory::inventory;

#endif
// File: main.cpp
#include "vehicle.h"

int main() {
    // ❌ 错误:不能直接创建
    // Vehicle v("Car", "Toyota", 2023, 25000);
    
    // ✅ 正确:通过工厂创建
    auto car1 = VehicleFactory::createCar("Toyota Camry", 2023, 25000);
    auto car2 = VehicleFactory::createCar("Honda Accord", 2022, 24000);
    auto truck1 = VehicleFactory::createTruck("Ford F-150", 2023, 35000);
    
    std::cout << "\nCreated vehicles:" << std::endl;
    std::cout << car1->getInfo() << std::endl;
    std::cout << car2->getInfo() << std::endl;
    std::cout << truck1->getInfo() << std::endl;
    
    VehicleFactory::printInventory();
    
    return 0;
}

📊 输出结果

[LOG] Vehicle created: Toyota Camry
[LOG] Vehicle created: Honda Accord
[LOG] Vehicle created: Ford F-150

Created vehicles:
Car - Toyota Camry (2023) - $25000
Car - Honda Accord (2022) - $24000
Truck - Ford F-150 (2023) - $35000

=== Vehicle Inventory ===
Car - Toyota Camry (2023) - $25000
Car - Honda Accord (2022) - $24000
Truck - Ford F-150 (2023) - $35000
Total: 3 vehicles
[LOG] Vehicle destroyed: Ford F-150
[LOG] Vehicle destroyed: Honda Accord
[LOG] Vehicle destroyed: Toyota Camry

场景5:单例模式

🎯 为什么用友元?

确保只有一个实例,防止外部直接创建对象。

✅ 完整代码示例

// File: logger.h
#ifndef LOGGER_H
#define LOGGER_H

#include <iostream>
#include <fstream>
#include <string>
#include <mutex>
#include <vector>

class Logger {
    // 友元函数:获取唯一实例
    friend Logger& getLogger();
    friend void destroyLogger();
    
private:
    static Logger* instance;
    static std::mutex mutex;
    
    std::string logFile;
    std::vector<std::string> logBuffer;
    bool fileEnabled;
    
    // 私有构造函数
    Logger() : logFile("app.log"), fileEnabled(true) {
        std::cout << "[Logger] Initialized" << std::endl;
    }
    
    // 禁止拷贝和赋值
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;
    
public:
    void setLogFile(const std::string& file) {
        logFile = file;
    }
    
    void enableFileLogging(bool enable) {
        fileEnabled = enable;
    }
    
    void log(const std::string& message) {
        std::lock_guard<std::mutex> lock(mutex);
        
        std::string logEntry = "[LOG] " + message;
        logBuffer.push_back(logEntry);
        
        std::cout << logEntry << std::endl;
        
        if (fileEnabled) {
            std::ofstream ofs(logFile, std::ios::app);
            if (ofs.is_open()) {
                ofs << logEntry << std::endl;
                ofs.close();
            }
        }
    }
    
    void logError(const std::string& message) {
        log("[ERROR] " + message);
    }
    
    void logWarning(const std::string& message) {
        log("[WARNING] " + message);
    }
    
    void printHistory() const {
        std::cout << "\n=== Log History ===" << std::endl;
        for (const auto& entry : logBuffer) {
            std::cout << entry << std::endl;
        }
    }
    
    ~Logger() {
        std::cout << "[Logger] Destroyed" << std::endl;
    }
};

// 静态成员初始化
Logger* Logger::instance = nullptr;
std::mutex Logger::mutex;

// 获取单例实例
Logger& getLogger() {
    std::lock_guard<std::mutex> lock(Logger::mutex);
    if (Logger::instance == nullptr) {
        Logger::instance = new Logger();
    }
    return *Logger::instance;
}

// 销毁单例
void destroyLogger() {
    std::lock_guard<std::mutex> lock(Logger::mutex);
    delete Logger::instance;
    Logger::instance = nullptr;
}

#endif
// File: main.cpp
#include "logger.h"

void someFunction() {
    Logger& logger = getLogger();
    logger.log("Entering someFunction");
    // ... 做一些事情
    logger.log("Exiting someFunction");
}

int main() {
    Logger& logger = getLogger();
    
    logger.setLogFile("myapp.log");
    logger.log("Application started");
    logger.logWarning("Low memory warning");
    
    someFunction();
    
    logger.logError("Something went wrong!");
    logger.log("Application ending");
    
    logger.printHistory();
    
    destroyLogger();
    
    return 0;
}

📊 输出结果

[Logger] Initialized
[LOG] Application started
[LOG] [WARNING] Low memory warning
[LOG] Entering someFunction
[LOG] Exiting someFunction
[LOG] [ERROR] Something went wrong!
[LOG] Application ending

=== Log History ===
[LOG] Application started
[LOG] [WARNING] Low memory warning
[LOG] Entering someFunction
[LOG] Exiting someFunction
[LOG] [ERROR] Something went wrong!
[LOG] Application ending
[Logger] Destroyed

场景6:迭代器模式

🎯 为什么用友元?

迭代器需要访问容器的内部数据结构,但不应该是容器的成员。

✅ 完整代码示例

// File: simple_vector.h
#ifndef SIMPLE_VECTOR_H
#define SIMPLE_VECTOR_H

#include <iostream>
#include <memory>
#include <initializer_list>

template<typename T>
class SimpleVector;

template<typename T>
class VectorIterator {
    friend class SimpleVector<T>;
    
private:
    T* ptr;
    
    // 私有构造函数
    VectorIterator(T* p) : ptr(p) {}
    
public:
    T& operator*() const { return *ptr; }
    T* operator->() const { return ptr; }
    
    VectorIterator& operator++() {
        ++ptr;
        return *this;
    }
    
    VectorIterator operator++(int) {
        VectorIterator tmp = *this;
        ++ptr;
        return tmp;
    }
    
    VectorIterator& operator--() {
        --ptr;
        return *this;
    }
    
    bool operator==(const VectorIterator& other) const {
        return ptr == other.ptr;
    }
    
    bool operator!=(const VectorIterator& other) const {
        return ptr != other.ptr;
    }
};

template<typename T>
class SimpleVector {
    friend class VectorIterator<T>;
    
private:
    T* data;
    size_t size;
    size_t capacity;
    
    void resize(size_t newCapacity) {
        T* newData = new T[newCapacity];
        for (size_t i = 0; i < size; ++i) {
            newData[i] = data[i];
        }
        delete[] data;
        data = newData;
        capacity = newCapacity;
    }
    
public:
    SimpleVector() : data(nullptr), size(0), capacity(0) {}
    
    SimpleVector(std::initializer_list<T> init) 
        : data(new T[init.size()]), size(init.size()), capacity(init.size()) {
        size_t i = 0;
        for (const auto& elem : init) {
            data[i++] = elem;
        }
    }
    
    ~SimpleVector() {
        delete[] data;
    }
    
    // 禁止拷贝(简化示例)
    SimpleVector(const SimpleVector&) = delete;
    SimpleVector& operator=(const SimpleVector&) = delete;
    
    void pushBack(const T& value) {
        if (size >= capacity) {
            resize(capacity == 0 ? 4 : capacity * 2);
        }
        data[size++] = value;
    }
    
    T& operator[](size_t index) {
        return data[index];
    }
    
    const T& operator[](size_t index) const {
        return data[index];
    }
    
    size_t getSize() const { return size; }
    bool empty() const { return size == 0; }
    
    // 返回迭代器
    VectorIterator<T> begin() {
        return VectorIterator<T>(data);
    }
    
    VectorIterator<T> end() {
        return VectorIterator<T>(data + size);
    }
    
    VectorIterator<T> begin() const {
        return VectorIterator<T>(data);
    }
    
    VectorIterator<T> end() const {
        return VectorIterator<T>(data + size);
    }
};

#endif
// File: main.cpp
#include "simple_vector.h"

int main() {
    SimpleVector<int> vec = {1, 2, 3, 4, 5};
    
    // 范围 for 循环
    std::cout << "Range-based for: ";
    for (int val : vec) {
        std::cout << val << " ";
    }
    std::cout << std::endl;
    
    // 迭代器
    std::cout << "Iterator: ";
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 修改元素
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        *it *= 2;
    }
    
    std::cout << "After doubling: ";
    for (int val : vec) {
        std::cout << val << " ";
    }
    std::cout << std::endl;
    
    // 添加元素
    vec.pushBack(6);
    vec.pushBack(7);
    
    std::cout << "After adding: ";
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

📊 输出结果

Range-based for: 1 2 3 4 5 
Iterator: 1 2 3 4 5 
After doubling: 2 4 6 8 10 
After adding: 2 4 6 8 10 12 14 

场景7:矩阵/向量运算

🎯 为什么用友元?

矩阵运算需要访问多个矩阵的私有数据,且运算符需要对称性。

✅ 完整代码示例

// File: matrix.h
#ifndef MATRIX_H
#define MATRIX_H

#include <iostream>
#include <vector>
#include <stdexcept>

class Matrix {
    friend Matrix operator+(const Matrix&, const Matrix&);
    friend Matrix operator-(const Matrix&, const Matrix&);
    friend Matrix operator*(const Matrix&, const Matrix&);
    friend Matrix operator*(double, const Matrix&);
    friend Matrix operator*(const Matrix&, double);
    friend std::ostream& operator<<(std::ostream&, const Matrix&);
    
private:
    std::vector<std::vector<double>> data;
    size_t rows;
    size_t cols;
    
public:
    Matrix(size_t r, size_t c) : rows(r), cols(c) {
        data.resize(rows, std::vector<double>(cols, 0.0));
    }
    
    Matrix(std::initializer_list<std::initializer_list<double>> init) {
        rows = init.size();
        cols = rows > 0 ? init.begin()->size() : 0;
        data.resize(rows);
        size_t i = 0;
        for (const auto& row : init) {
            data[i].assign(row.begin(), row.end());
            ++i;
        }
    }
    
    double& at(size_t r, size_t c) {
        if (r >= rows || c >= cols) throw std::out_of_range("Index out of bounds");
        return data[r][c];
    }
    
    double at(size_t r, size_t c) const {
        if (r >= rows || c >= cols) throw std::out_of_range("Index out of bounds");
        return data[r][c];
    }
    
    size_t getRows() const { return rows; }
    size_t getCols() const { return cols; }
    
    Matrix transpose() const {
        Matrix result(cols, rows);
        for (size_t i = 0; i < rows; ++i) {
            for (size_t j = 0; j < cols; ++j) {
                result.data[j][i] = data[i][j];
            }
        }
        return result;
    }
};

// 矩阵加法
Matrix operator+(const Matrix& a, const Matrix& b) {
    if (a.rows != b.rows || a.cols != b.cols) {
        throw std::invalid_argument("Matrix dimensions must match for addition");
    }
    Matrix result(a.rows, a.cols);
    for (size_t i = 0; i < a.rows; ++i) {
        for (size_t j = 0; j < a.cols; ++j) {
            result.data[i][j] = a.data[i][j] + b.data[i][j];
        }
    }
    return result;
}

// 矩阵减法
Matrix operator-(const Matrix& a, const Matrix& b) {
    if (a.rows != b.rows || a.cols != b.cols) {
        throw std::invalid_argument("Matrix dimensions must match for subtraction");
    }
    Matrix result(a.rows, a.cols);
    for (size_t i = 0; i < a.rows; ++i) {
        for (size_t j = 0; j < a.cols; ++j) {
            result.data[i][j] = a.data[i][j] - b.data[i][j];
        }
    }
    return result;
}

// 矩阵乘法
Matrix operator*(const Matrix& a, const Matrix& b) {
    if (a.cols != b.rows) {
        throw std::invalid_argument("Matrix dimensions incompatible for multiplication");
    }
    Matrix result(a.rows, b.cols);
    for (size_t i = 0; i < a.rows; ++i) {
        for (size_t j = 0; j < b.cols; ++j) {
            for (size_t k = 0; k < a.cols; ++k) {
                result.data[i][j] += a.data[i][k] * b.data[k][j];
            }
        }
    }
    return result;
}

// 标量 * 矩阵
Matrix operator*(double scalar, const Matrix& m) {
    Matrix result(m.rows, m.cols);
    for (size_t i = 0; i < m.rows; ++i) {
        for (size_t j = 0; j < m.cols; ++j) {
            result.data[i][j] = scalar * m.data[i][j];
        }
    }
    return result;
}

// 矩阵 * 标量
Matrix operator*(const Matrix& m, double scalar) {
    return scalar * m;
}

// 输出
std::ostream& operator<<(std::ostream& os, const Matrix& m) {
    for (size_t i = 0; i < m.rows; ++i) {
        os << "| ";
        for (size_t j = 0; j < m.cols; ++j) {
            os << m.data[i][j] << " ";
        }
        os << "|" << std::endl;
    }
    return os;
}

#endif
// File: main.cpp
#include "matrix.h"

int main() {
    Matrix a = {{1, 2}, {3, 4}};
    Matrix b = {{5, 6}, {7, 8}};
    
    std::cout << "Matrix A:" << std::endl << a << std::endl;
    std::cout << "Matrix B:" << std::endl << b << std::endl;
    
    std::cout << "A + B:" << std::endl << (a + b) << std::endl;
    std::cout << "A - B:" << std::endl << (a - b) << std::endl;
    std::cout << "A * B:" << std::endl << (a * b) << std::endl;
    
    std::cout << "2 * A:" << std::endl << (2 * a) << std::endl;
    std::cout << "A * 2:" << std::endl << (a * 2) << std::endl;
    
    std::cout << "A transpose:" << std::endl << a.transpose() << std::endl;
    
    return 0;
}

📊 输出结果

Matrix A:
| 1 2 |
| 3 4 |

Matrix B:
| 5 6 |
| 7 8 |

A + B:
| 6 8 |
| 10 12 |

A - B:
| -4 -4 |
| -4 -4 |

A * B:
| 19 22 |
| 43 50 |

2 * A:
| 2 4 |
| 6 8 |

A * 2:
| 2 4 |
| 6 8 |

A transpose:
| 1 3 |
| 2 4 |

场景8:序列化/反序列化

🎯 为什么用友元?

序列化需要访问对象的所有私有数据,但不应该是类的成员函数(便于扩展不同格式)。

✅ 完整代码示例

// File: serializable.h
#ifndef SERIALIZABLE_H
#define SERIALIZABLE_H

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

class Person;

// 序列化函数声明
std::string serialize(const Person& p);
Person deserialize(const std::string& data);
bool saveToFile(const Person& p, const std::string& filename);
Person loadFromFile(const std::string& filename);

class Person {
    friend std::string serialize(const Person& p);
    friend Person deserialize(const std::string& data);
    friend bool saveToFile(const Person& p, const std::string& filename);
    friend Person loadFromFile(const std::string& filename);
    
private:
    std::string name;
    int age;
    std::string email;
    std::vector<std::string> hobbies;
    
public:
    Person() : age(0) {}
    Person(const std::string& n, int a, const std::string& e)
        : name(n), age(a), email(e) {}
    
    void addHobby(const std::string& hobby) {
        hobbies.push_back(hobby);
    }
    
    void print() const {
        std::cout << "Name: " << name << ", Age: " << age 
                  << ", Email: " << email << std::endl;
        std::cout << "Hobbies: ";
        for (const auto& h : hobbies) {
            std::cout << h << " ";
        }
        std::cout << std::endl;
    }
};

// JSON 格式序列化
std::string serialize(const Person& p) {
    std::ostringstream oss;
    oss << "{\"name\":\"" << p.name << "\","
        << "\"age\":" << p.age << ","
        << "\"email\":\"" << p.email << "\","
        << "\"hobbies\":[";
    for (size_t i = 0; i < p.hobbies.size(); ++i) {
        if (i > 0) oss << ",";
        oss << "\"" << p.hobbies[i] << "\"";
    }
    oss << "]}";
    return oss.str();
}

// JSON 格式反序列化(简化版)
Person deserialize(const std::string& data) {
    Person p;
    // 简化解析(实际项目应使用 JSON 库)
    size_t nameStart = data.find("\"name\":\"") + 8;
    size_t nameEnd = data.find("\"", nameStart);
    p.name = data.substr(nameStart, nameEnd - nameStart);
    
    size_t ageStart = data.find("\"age\":") + 6;
    size_t ageEnd = data.find(",", ageStart);
    p.age = std::stoi(data.substr(ageStart, ageEnd - ageStart));
    
    size_t emailStart = data.find("\"email\":\"") + 9;
    size_t emailEnd = data.find("\"", emailStart);
    p.email = data.substr(emailStart, emailEnd - emailStart);
    
    return p;
}

// 保存到文件
bool saveToFile(const Person& p, const std::string& filename) {
    std::ofstream file(filename);
    if (!file.is_open()) return false;
    file << serialize(p);
    file.close();
    return true;
}

// 从文件加载
Person loadFromFile(const std::string& filename) {
    std::ifstream file(filename);
    std::string content((std::istreambuf_iterator<char>(file)),
                        std::istreambuf_iterator<char>());
    file.close();
    return deserialize(content);
}

#endif
// File: main.cpp
#include "serializable.h"

int main() {
    Person p1("Alice", 25, "alice@example.com");
    p1.addHobby("Reading");
    p1.addHobby("Coding");
    p1.addHobby("Hiking");
    
    std::cout << "Original Person:" << std::endl;
    p1.print();
    
    // 序列化
    std::string json = serialize(p1);
    std::cout << "\nSerialized JSON:" << std::endl;
    std::cout << json << std::endl;
    
    // 保存到文件
    if (saveToFile(p1, "person.json")) {
        std::cout << "\nSaved to person.json" << std::endl;
    }
    
    // 从文件加载
    Person p2 = loadFromFile("person.json");
    std::cout << "\nLoaded Person:" << std::endl;
    p2.print();
    
    // 反序列化
    Person p3 = deserialize(json);
    std::cout << "\nDeserialized Person:" << std::endl;
    p3.print();
    
    return 0;
}

📊 输出结果

Original Person:
Name: Alice, Age: 25, Email: alice@example.com
Hobbies: Reading Coding Hiking 

Serialized JSON:
{"name":"Alice","age":25,"email":"alice@example.com","hobbies":["Reading","Coding","Hiking"]}

Saved to person.json

Loaded Person:
Name: Alice, Age: 25, Email: alice@example.com
Hobbies: 

Deserialized Person:
Name: Alice, Age: 25, Email: alice@example.com
Hobbies: 

场景9:测试框架

🎯 为什么用友元?

测试代码需要访问被测试类的私有成员进行验证,但不应该修改生产代码的访问权限。

✅ 完整代码示例

// File: calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H

#include <iostream>
#include <vector>

class CalculatorTest;

class Calculator {
    friend class CalculatorTest;
    
private:
    double memory;
    std::vector<double> history;
    int operationCount;
    
    double internalCalculate(double a, double b, char op) {
        double result = 0;
        switch (op) {
            case '+': result = a + b; break;
            case '-': result = a - b; break;
            case '*': result = a * b; break;
            case '/': result = (b != 0) ? a / b : 0; break;
        }
        return result;
    }
    
public:
    Calculator() : memory(0), operationCount(0) {}
    
    double add(double a, double b) {
        double result = internalCalculate(a, b, '+');
        history.push_back(result);
        operationCount++;
        return result;
    }
    
    double subtract(double a, double b) {
        double result = internalCalculate(a, b, '-');
        history.push_back(result);
        operationCount++;
        return result;
    }
    
    double multiply(double a, double b) {
        double result = internalCalculate(a, b, '*');
        history.push_back(result);
        operationCount++;
        return result;
    }
    
    double divide(double a, double b) {
        double result = internalCalculate(a, b, '/');
        history.push_back(result);
        operationCount++;
        return result;
    }
    
    void storeToMemory(double value) {
        memory = value;
    }
    
    double recallFromMemory() const {
        return memory;
    }
    
    int getOperationCount() const {
        return operationCount;
    }
};

// 测试类
class CalculatorTest {
public:
    static void testInternalCalculate() {
        Calculator calc;
        
        std::cout << "Testing internalCalculate..." << std::endl;
        
        double result = calc.internalCalculate(10, 5, '+');
        std::cout << "10 + 5 = " << result << " (expected: 15)" << std::endl;
        
        result = calc.internalCalculate(10, 5, '-');
        std::cout << "10 - 5 = " << result << " (expected: 5)" << std::endl;
        
        result = calc.internalCalculate(10, 5, '*');
        std::cout << "10 * 5 = " << result << " (expected: 50)" << std::endl;
        
        result = calc.internalCalculate(10, 5, '/');
        std::cout << "10 / 5 = " << result << " (expected: 2)" << std::endl;
    }
    
    static void testPrivateMembers() {
        Calculator calc;
        
        calc.add(1, 2);
        calc.add(3, 4);
        calc.multiply(2, 3);
        
        std::cout << "\nTesting private members access..." << std::endl;
        std::cout << "Operation count: " << calc.operationCount << " (expected: 3)" << std::endl;
        std::cout << "History size: " << calc.history.size() << " (expected: 3)" << std::endl;
        std::cout << "Memory value: " << calc.memory << " (expected: 0)" << std::endl;
        
        calc.storeToMemory(100);
        std::cout << "After storing 100, memory: " << calc.memory << " (expected: 100)" << std::endl;
    }
    
    static void runAllTests() {
        std::cout << "=== Calculator Test Suite ===" << std::endl;
        testInternalCalculate();
        testPrivateMembers();
        std::cout << "\n=== All Tests Completed ===" << std::endl;
    }
};

#endif
// File: main.cpp
#include "calculator.h"

int main() {
    // 运行测试
    CalculatorTest::runAllTests();
    
    // 正常使用
    std::cout << "\n=== Normal Usage ===" << std::endl;
    Calculator calc;
    std::cout << "5 + 3 = " << calc.add(5, 3) << std::endl;
    std::cout << "10 - 4 = " << calc.subtract(10, 4) << std::endl;
    std::cout << "6 * 7 = " << calc.multiply(6, 7) << std::endl;
    std::cout << "20 / 4 = " << calc.divide(20, 4) << std::endl;
    
    return 0;
}

📊 输出结果

=== Calculator Test Suite ===
Testing internalCalculate...
10 + 5 = 15 (expected: 15)
10 - 5 = 5 (expected: 5)
10 * 5 = 50 (expected: 50)
10 / 5 = 2 (expected: 2)

Testing private members access...
Operation count: 3 (expected: 3)
History size: 3 (expected: 3)
Memory value: 0 (expected: 0)
After storing 100, memory: 100 (expected: 100)

=== All Tests Completed ===

=== Normal Usage ===
5 + 3 = 8
10 - 4 = 6
6 * 7 = 42
20 / 4 = 5

场景10:缓存系统

🎯 为什么用友元?

缓存管理器需要访问被缓存对象的内部状态来判断是否有效、是否需要更新。

✅ 完整代码示例

// File: cache.h
#ifndef CACHE_H
#define CACHE_H

#include <iostream>
#include <string>
#include <unordered_map>
#include <chrono>
#include <memory>

template<typename T>
class CacheManager;

template<typename T>
class CachedObject {
    friend class CacheManager<T>;
    
private:
    T data;
    std::chrono::system_clock::time_point createdAt;
    std::chrono::system_clock::time_point lastAccessed;
    int accessCount;
    bool isValid;
    std::string key;
    
    CachedObject(const std::string& k, const T& d)
        : data(d), key(k), accessCount(0), isValid(true) {
        createdAt = lastAccessed = std::chrono::system_clock::now();
    }
    
    void markAccessed() {
        lastAccessed = std::chrono::system_clock::now();
        accessCount++;
    }
    
    void invalidate() {
        isValid = false;
    }
    
public:
    const T& getData() const { return data; }
    std::string getKey() const { return key; }
    int getAccessCount() const { return accessCount; }
    
    std::chrono::seconds getAge() const {
        auto now = std::chrono::system_clock::now();
        return std::chrono::duration_cast<std::chrono::seconds>(now - createdAt);
    }
};

template<typename T>
class CacheManager {
private:
    std::unordered_map<std::string, std::shared_ptr<CachedObject<T>>> cache;
    size_t maxSize;
    std::chrono::seconds maxAge;
    
    void evictIfNeeded() {
        if (cache.size() <= maxSize) return;
        
        // 简单的 LRU 策略:删除最久未访问的
        std::string oldestKey;
        auto oldestTime = std::chrono::system_clock::now();
        
        for (const auto& pair : cache) {
            if (pair.second->lastAccessed < oldestTime) {
                oldestTime = pair.second->lastAccessed;
                oldestKey = pair.first;
            }
        }
        
        if (!oldestKey.empty()) {
            std::cout << "[Cache] Evicting: " << oldestKey << std::endl;
            cache.erase(oldestKey);
        }
    }
    
    void removeExpired() {
        auto now = std::chrono::system_clock::now();
        for (auto it = cache.begin(); it != cache.end(); ) {
            auto age = std::chrono::duration_cast<std::chrono::seconds>(
                now - it->second->createdAt);
            if (age > maxAge) {
                std::cout << "[Cache] Expired: " << it->first << std::endl;
                it = cache.erase(it);
            } else {
                ++it;
            }
        }
    }
    
public:
    CacheManager(size_t size = 100, int ageSeconds = 3600)
        : maxSize(size), maxAge(ageSeconds) {}
    
    void put(const std::string& key, const T& value) {
        removeExpired();
        evictIfNeeded();
        
        auto obj = std::make_shared<CachedObject<T>>(key, value);
        cache[key] = obj;
        std::cout << "[Cache] Stored: " << key << std::endl;
    }
    
    T get(const std::string& key) {
        auto it = cache.find(key);
        if (it != cache.end() && it->second->isValid) {
            it->second->markAccessed();  // ✅ 访问私有成员
            std::cout << "[Cache] Hit: " << key << std::endl;
            return it->second->data;
        }
        std::cout << "[Cache] Miss: " << key << std::endl;
        return T();
    }
    
    void invalidate(const std::string& key) {
        auto it = cache.find(key);
        if (it != cache.end()) {
            it->second->invalidate();  // ✅ 访问私有成员
            std::cout << "[Cache] Invalidated: " << key << std::endl;
        }
    }
    
    void printStats() const {
        std::cout << "\n=== Cache Statistics ===" << std::endl;
        std::cout << "Size: " << cache.size() << "/" << maxSize << std::endl;
        for (const auto& pair : cache) {
            std::cout << "  " << pair.first 
                      << " (age: " << pair.second->getAge().count() 
                      << "s, accesses: " << pair.second->accessCount << ")" << std::endl;
        }
    }
};

#endif
// File: main.cpp
#include "cache.h"
#include <thread>

int main() {
    CacheManager<std::string> cache(5, 10);  // 最大5个,10秒过期
    
    // 存入数据
    cache.put("user:1", "Alice");
    cache.put("user:2", "Bob");
    cache.put("user:3", "Charlie");
    
    // 读取数据
    std::cout << "\nGetting user:1: " << cache.get("user:1") << std::endl;
    std::cout << "Getting user:2: " << cache.get("user:2") << std::endl;
    std::cout << "Getting user:4: " << cache.get("user:4") << std::endl;  // Miss
    
    // 再次读取(增加访问计数)
    cache.get("user:1");
    cache.get("user:1");
    
    // 使一个缓存失效
    cache.invalidate("user:2");
    cache.get("user:2");  // 应该 Miss
    
    // 等待过期
    std::cout << "\nWaiting 11 seconds for expiration..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(11));
    
    cache.get("user:1");  // 应该过期了
    cache.get("user:3");  // 应该过期了
    
    cache.printStats();
    
    return 0;
}

📊 输出结果

[Cache] Stored: user:1
[Cache] Stored: user:2
[Cache] Stored: user:3

[Cache] Hit: user:1
Getting user:1: Alice
[Cache] Hit: user:2
Getting user:2: Bob
[Cache] Miss: user:4
Getting user:4: 
[Cache] Hit: user:1
[Cache] Hit: user:1
[Cache] Invalidated: user:2
[Cache] Miss: user:2

Waiting 11 seconds for expiration...
[Cache] Miss: user:1
[Cache] Miss: user:3

=== Cache Statistics ===
Size: 0/5

场景11:观察者模式

🎯 为什么用友元?

被观察者需要访问观察者的私有更新方法,观察者需要访问被观察者的私有状态。

✅ 完整代码示例

// File: observer.h
#ifndef OBSERVER_H
#define OBSERVER_H

#include <iostream>
#include <string>
#include <vector>
#include <memory>

class Subject;

class Observer {
    friend class Subject;
    
private:
    std::string name;
    std::string lastUpdate;
    
protected:
    virtual void update(const std::string& message) {
        lastUpdate = message;
        std::cout << "[" << name << "] Received: " << message << std::endl;
    }
    
public:
    Observer(const std::string& n) : name(n) {}
    virtual ~Observer() = default;
    
    std::string getName() const { return name; }
    std::string getLastUpdate() const { return lastUpdate; }
};

class Subject {
    friend class ConcreteObserver;
    
private:
    std::vector<std::shared_ptr<Observer>> observers;
    std::string state;
    int stateVersion;
    
public:
    Subject() : stateVersion(0) {}
    
    void attach(std::shared_ptr<Observer> obs) {
        observers.push_back(obs);
        std::cout << "[Subject] Attached observer: " << obs->name << std::endl;
    }
    
    void detach(const std::string& name) {
        observers.erase(
            std::remove_if(observers.begin(), observers.end(),
                [&name](const std::shared_ptr<Observer>& obs) {
                    return obs->name == name;
                }),
            observers.end()
        );
        std::cout << "[Subject] Detached observer: " << name << std::endl;
    }
    
    void setState(const std::string& newState) {
        state = newState;
        stateVersion++;
        std::cout << "[Subject] State changed to: " << state << std::endl;
        notify();
    }
    
    void notify() {
        std::string message = "State v" + std::to_string(stateVersion) + ": " + state;
        for (auto& obs : observers) {
            obs->update(message);  // ✅ 访问 Observer 的 protected 方法
        }
    }
    
    std::string getState() const { return state; }
    int getStateVersion() const { return stateVersion; }
};

class ConcreteObserver : public Observer {
private:
    int updateCount;
    
public:
    ConcreteObserver(const std::string& n) : Observer(n), updateCount(0) {}
    
    void update(const std::string& message) override {
        Observer::update(message);
        updateCount++;
        std::cout << "[" << name << "] Update count: " << updateCount << std::endl;
    }
    
    int getUpdateCount() const { return updateCount; }
};

#endif
// File: main.cpp
#include "observer.h"
#include <algorithm>

int main() {
    Subject subject;
    
    auto obs1 = std::make_shared<ConcreteObserver>("Observer1");
    auto obs2 = std::make_shared<ConcreteObserver>("Observer2");
    auto obs3 = std::make_shared<ConcreteObserver>("Observer3");
    
    subject.attach(obs1);
    subject.attach(obs2);
    subject.attach(obs3);
    
    std::cout << "\n--- Changing state ---" << std::endl;
    subject.setState("Initial State");
    
    std::cout << "\n--- Changing state again ---" << std::endl;
    subject.setState("Updated State");
    
    std::cout << "\n--- Detaching Observer2 ---" << std::endl;
    subject.detach("Observer2");
    
    std::cout << "\n--- Changing state after detach ---" << std::endl;
    subject.setState("Final State");
    
    std::cout << "\n=== Observer Statistics ===" << std::endl;
    std::cout << "Observer1 updates: " << obs1->getUpdateCount() << std::endl;
    std::cout << "Observer2 updates: " << obs2->getUpdateCount() << std::endl;
    std::cout << "Observer3 updates: " << obs3->getUpdateCount() << std::endl;
    
    return 0;
}

📊 输出结果

[Subject] Attached observer: Observer1
[Subject] Attached observer: Observer2
[Subject] Attached observer: Observer3

--- Changing state ---
[Subject] State changed to: Initial State
[Observer1] Received: State v1: Initial State
[Observer1] Update count: 1
[Observer2] Received: State v1: Initial State
[Observer2] Update count: 1
[Observer3] Received: State v1: Initial State
[Observer3] Update count: 1

--- Changing state again ---
[Subject] State changed to: Updated State
[Observer1] Received: State v2: Updated State
[Observer1] Update count: 2
[Observer2] Received: State v2: Updated State
[Observer2] Update count: 2
[Observer3] Received: State v2: Updated State
[Observer3] Update count: 2

--- Detaching Observer2 ---
[Subject] Detached observer: Observer2

--- Changing state after detach ---
[Subject] State changed to: Final State
[Observer1] Received: State v3: Final State
[Observer1] Update count: 3
[Observer3] Received: State v3: Final State
[Observer3] Update count: 3

=== Observer Statistics ===
Observer1 updates: 3
Observer2 updates: 2
Observer3 updates: 3

场景12:智能指针

🎯 为什么用友元?

智能指针需要访问被管理对象的私有析构函数和引用计数。

✅ 完整代码示例

// File: smart_ptr.h
#ifndef SMART_PTR_H
#define SMART_PTR_H

#include <iostream>
#include <atomic>

template<typename T>
class SharedPtr;

template<typename T>
class ControlBlock {
    friend class SharedPtr<T>;
    
private:
    T* ptr;
    std::atomic<int> refCount;
    
    ControlBlock(T* p) : ptr(p), refCount(1) {}
    
    ~ControlBlock() {
        delete ptr;
    }
};

template<typename T>
class SharedPtr {
private:
    ControlBlock<T>* control;
    
    void release() {
        if (control && --control->refCount == 0) {
            delete control;
        }
    }
    
public:
    explicit SharedPtr(T* ptr = nullptr) 
        : control(ptr ? new ControlBlock<T>(ptr) : nullptr) {}
    
    // 拷贝构造函数
    SharedPtr(const SharedPtr& other) : control(other.control) {
        if (control) {
            control->refCount++;
        }
    }
    
    // 拷贝赋值
    SharedPtr& operator=(const SharedPtr& other) {
        if (this != &other) {
            release();
            control = other.control;
            if (control) {
                control->refCount++;
            }
        }
        return *this;
    }
    
    // 移动构造函数
    SharedPtr(SharedPtr&& other) noexcept : control(other.control) {
        other.control = nullptr;
    }
    
    // 移动赋值
    SharedPtr& operator=(SharedPtr&& other) noexcept {
        if (this != &other) {
            release();
            control = other.control;
            other.control = nullptr;
        }
        return *this;
    }
    
    T& operator*() const { return *control->ptr; }
    T* operator->() const { return control->ptr; }
    T* get() const { return control ? control->ptr : nullptr; }
    
    int useCount() const {
        return control ? control->refCount.load() : 0;
    }
    
    ~SharedPtr() {
        release();
    }
};

// 测试类
class TestClass {
    friend class ControlBlock<TestClass>;
    
private:
    int value;
    std::string name;
    
public:
    TestClass(int v, const std::string& n) : value(v), name(n) {
        std::cout << "[TestClass] Created: " << name << std::endl;
    }
    
    ~TestClass() {
        std::cout << "[TestClass] Destroyed: " << name << std::endl;
    }
    
    int getValue() const { return value; }
    std::string getName() const { return name; }
};

#endif
// File: main.cpp
#include "smart_ptr.h"

void testSharedPtr() {
    std::cout << "=== SharedPtr Test ===" << std::endl;
    
    SharedPtr<TestClass> p1(new TestClass(10, "Object1"));
    std::cout << "p1 use count: " << p1.useCount() << std::endl;
    
    {
        SharedPtr<TestClass> p2 = p1;
        std::cout << "p2 use count: " << p2.useCount() << std::endl;
        
        SharedPtr<TestClass> p3 = p2;
        std::cout << "p3 use count: " << p3.useCount() << std::endl;
        
        std::cout << "Value: " << p3->getValue() << std::endl;
    }
    
    std::cout << "After p2, p3 destroyed, p1 use count: " << p1.useCount() << std::endl;
}

void testMove() {
    std::cout << "\n=== Move Test ===" << std::endl;
    
    SharedPtr<TestClass> p1(new TestClass(20, "Object2"));
    std::cout << "p1 use count: " << p1.useCount() << std::endl;
    
    SharedPtr<TestClass> p2 = std::move(p1);
    std::cout << "After move, p1 use count: " << p1.useCount() << std::endl;
    std::cout << "After move, p2 use count: " << p2.useCount() << std::endl;
    std::cout << "Value: " << p2->getValue() << std::endl;
}

int main() {
    testSharedPtr();
    testMove();
    
    std::cout << "\n=== Program End ===" << std::endl;
    return 0;
}

📊 输出结果

=== SharedPtr Test ===
[TestClass] Created: Object1
p1 use count: 1
p2 use count: 2
p3 use count: 3
Value: 10
[TestClass] Destroyed: Object1
After p2, p3 destroyed, p1 use count: 1

=== Move Test ===
[TestClass] Created: Object2
p1 use count: 1
After move, p1 use count: 0
After move, p2 use count: 1
Value: 20
[TestClass] Destroyed: Object2

=== Program End ===
[TestClass] Destroyed: Object1

📋 友元使用决策树

需要访问私有成员?
│
├─ 否 → 不需要友元 ✅
│
└─ 是 → 该函数应该是成员函数吗?
        │
        ├─ 是 → 定义为成员函数 ✅
        │
        └─ 否 → 左操作数必须是其他类型吗?(如 ostream)
                │
                ├─ 是 → 使用友元函数 ✅(场景1)
                │
                └─ 否 → 需要对称性吗?(如 a+b 和 b+a)
                        │
                        ├─ 是 → 使用友元函数 ✅(场景2)
                        │
                        └─ 否 → 需要访问多个类的私有成员吗?
                                │
                                ├─ 是 → 使用友元函数 ✅(场景3)
                                │
                                └─ 否 → 是工厂/单例/测试吗?
                                        │
                                        ├─ 是 → 使用友元 ✅(场景4-5-9)
                                        │
                                        └─ 否 → 考虑公有接口 ✅

⚠️ 友元使用陷阱与最佳实践

陷阱1:过度使用友元

// ❌ 坏设计:所有函数都是友元
class MyClass {
    friend void func1(MyClass&);
    friend void func2(MyClass&);
    friend void func3(MyClass&);
    friend void func4(MyClass&);
    // ... 太多友元 = 没有封装
private:
    int data;
};

// ✅ 好设计:最小化友元
class MyClass {
    friend std::ostream& operator<<(std::ostream&, const MyClass&);
private:
    int data;
public:
    int getData() const { return data; }
    void setData(int d) { data = d; }
};

陷阱2:友元不传递

class A {
    friend void func(A&);
private:
    int x;
};

class B {
    // func 不是 B 的友元!
private:
    int y;
};

void func(A& a) {
    a.x = 10;  // ✅ 可以
    // b.y = 20;  // ❌ 错误
}

陷阱3:友元不继承

class Base {
    friend void func(Base&);
protected:
    int x;
};

class Derived : public Base {
    // func 不是 Derived 的友元!
private:
    int y;
};

最佳实践清单

原则说明
最小化只声明必要的友元
文档化注释说明为什么需要这个友元
优先公有接口能通过公有成员解决的,不用友元
避免友元类友元函数比友元类更安全
谨慎跨模块不同库之间尽量避免友元
测试隔离测试代码用友元,生产代码不用

🎓 总结

场景必要性推荐度关键原因
输入输出运算符必须⭐⭐⭐⭐⭐左操作数不是类对象
对称运算符必须⭐⭐⭐⭐⭐保证 a+b 和 b+a 都工作
跨类操作必须⭐⭐⭐⭐访问多个类私有成员
工厂模式推荐⭐⭐⭐⭐控制对象创建
单例模式推荐⭐⭐⭐⭐防止外部创建
迭代器推荐⭐⭐⭐⭐⭐访问容器内部结构
矩阵运算推荐⭐⭐⭐⭐对称性和多对象访问
序列化推荐⭐⭐⭐⭐⭐访问所有私有数据
测试框架推荐⭐⭐⭐⭐验证私有成员
缓存系统推荐⭐⭐⭐⭐管理对象状态
观察者模式推荐⭐⭐⭐⭐双向访问需求
智能指针必须⭐⭐⭐⭐⭐管理对象生命周期

记住:友元是打破封装的"特权",应该像使用特权一样谨慎! 🔒

关注我,后面更精彩。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张工在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值