友元类是为解决特定协作场景下深度交互与封装保护的矛盾而设,如容器与迭代器、类簇构造、单元测试等,需克制使用且明确声明。

友元类不是为了“打破封装”,而是为了解决特定协作场景下,类与类之间需要深度交互但又不便暴露公有接口的矛盾。它的使用应当克制、明确且有充分理由。
需要共享内部数据结构的紧密耦合组件
当两个类在逻辑上构成一个整体,比如容器类和它的迭代器类,迭代器必须能直接访问容器的底层指针或节点结构,但把所有成员设为 public 会破坏容器的数据完整性约束。此时将迭代器声明为容器的友元类,既保证了访问效率,又维持了封装边界——只有迭代器能越界访问,其他外部代码依然被隔离。
- 典型例子:std::vector 的私有成员 _M_start、_M_finish 是其 iterator 可直接读写的,但普通用户代码完全不可见
- 注意:友元关系不传递,也不继承;A 是 B 的友元,不意味着 A 的子类或 B 的子类自动获得该权限
实现类簇(Class Cluster)时统一构造与资源管理
某些设计中,对外只暴露一个抽象接口类,而实际由多个具体实现类分工协作(如不同压缩格式的解码器)。主接口类可能需要创建并初始化这些实现类,并直接设置其私有状态(如缓冲区地址、解码上下文),但又不希望把这些初始化逻辑暴露为 public 接口。这时可将主接口类声明为各实现类的友元。
- 避免把构造参数全塞进构造函数,也避免为初始化过程添加冗余的 set_xxx() 方法
- 关键点:友元仅用于构造/配置阶段的可信协作,运行时仍通过公有接口通信
单元测试类需验证私有行为逻辑
测试代码本身不属于产品模块,但需要检查类的私有成员是否按预期更新(例如缓存命中计数、状态机当前 stage)。将测试类(如 TestMyClass)声明为被测类的友元,可在测试中直接断言私有变量值,而不必为测试破环设计——无需添加仅供测试用的 public getter。
立即学习“C++免费学习笔记(深入)”;
- 建议将测试类命名清晰(如 MyClassTest),并在头文件中用 #ifdef TEST 宏控制友元声明,确保发布版不包含该关系
- 这不是偷懒替代测试设计,而是对关键不变量做白盒验证的合理手段
基本上就这些。友元类不是语法糖,而是协作契约——它让两个类在编译期就约定好“我信你,只信你”。用对了,代码更清晰;滥用,则封装形同虚设。










