friend class 在特定协作场景下合理使用可避免暴露接口,如 iterator 访问 container 私有数组;需前向声明、不传递、不继承、增加头文件依赖;friend function 适合对称操作如 operator。

friend class 会打破封装,但只在特定协作场景下值得用
它让另一个类能直接访问当前类的所有私有成员,不是为了偷懒绕过访问控制,而是解决两个紧密耦合类之间“不得不共享内部状态”的问题。比如 Iterator 类需要遍历 Container 的底层数组,而这个数组必须是私有的——这时把 Iterator 声明为 Container 的 friend class,比暴露 get_raw_data() 或把数组设为 public 更干净。
常见错误现象:friend class B; 写在 A 类定义里,但编译报错说 B 未定义——这是因为 B 类型名在声明时还不可见,得提前声明 class B;(前向声明)。
- 只能在类定义内部声明,不能在类外补加
- 不具有传递性:A 是 B 的 friend,B 是 C 的 friend,不等于 A 能访问 C 的私有成员
- 不会继承:派生类不会自动获得基类 friend 的权限
- 头文件依赖变重:
friend class X;意味着你得 #include X 的定义,或至少前向声明
friend function 适合做对称操作,比如 operator
当某个函数逻辑上不属于任何一方、又需要访问双方私有成员时,friend 函数是唯一选择。最典型的是流输出运算符:operator 左操作数是 <code>std::ostream&,不可能把它塞进你的类里;但它又得读你的私有字段,所以必须声明为 friend。
使用场景举例:实现两个自定义类的相等比较 operator==(const A&, const B&),且双方都有不可公开的内部标识字段。
立即学习“C++免费学习笔记(深入)”;
- 声明时写全签名:
friend std::ostream& operator - 定义可以放在类外(通常在 .cpp 里),不用加
friend关键字 - 如果函数模板要成为 friend,需显式指定模板参数或使用友元模板声明,否则容易因 ADL 失效而调用不到
- 避免把普通工具函数(如
validate())设为 friend——它本该通过 public 接口完成工作
别用 friend 绕过设计缺陷,否则私有字段形同虚设
看到 “我需要访问 private 成员” 就加 friend,大概率说明类职责没划清。比如一个 Database 类把连接句柄藏成私有,结果所有业务类都申请成为它的 friend 去执行 SQL——这其实是 Database 缺少足够粒度的 public 方法(如 execute_query()),而不是 friend 用得不够多。
容易踩的坑:
- 测试类(如
MyClassTest)不该是 friend:应测 public 行为,而非钻进去检查私有字段值 - 把整个第三方库的命名空间设为 friend(
friend namespace boost;)——C++ 不支持,而且极其危险 - 误以为 friend 提升性能:它只是取消访问检查,不影响内联、拷贝或内存布局
- 在模板类中声明 friend 时,若未用
template<typename t></typename>显式关联,会导致特化版本无法访问
替代方案往往更可持续:组合 + 精细接口 > 暴露内部
多数时候,与其拉一个 friend 进来,不如在类里加一个受控的访问入口。比如把 std::vector<int> data_;</int> 私有成员,换成 const std::vector<int>& raw_data() const { return data_; }</int> ——既不破坏封装,又满足只读需求。
性能影响很小:返回 const 引用几乎零开销;如果担心别名问题,可用 std::span<const int></const> 替代(C++20)。
- 优先提供 const 访问器,而非非 const 的 getter
- 用
std::optional<t></t>或状态码表达“可能不存在”的访问结果,比返回裸指针安全 - 对调试用途的私有状态,考虑用宏控制是否编译进 release 版本,而不是靠 friend 开后门
真正难处理的是跨模块的深度协作——比如解析器和语法树节点。这时候 friend 不是捷径,而是明确的契约:你得同步维护双方的内部变更,稍有不慎就崩。这种耦合,写注释比写代码还重要。










