friend函数可访问private成员,但仅限显式声明的函数或类,不传递、不继承,需谨慎使用以维护封装性。

friend 函数能访问 private 成员,但不能绕过访问控制语义
友元不是“后门”,而是显式授权。声明 friend 的函数或类,编译器会允许它直接读写当前类的 private 和 protected 成员——但仅限于你明确写出的那些声明,不会自动传导,也不影响封装契约本身。
常见错误是以为加了 friend 就能“随便进”:比如在基类里声明 friend void f();,子类的私有成员依然对 f() 不可见;或者误以为 friend 能让外部函数调用私有构造函数而不受限制(其实仍需满足构造逻辑,比如参数匹配、explicit 限制等)。
-
friend声明必须出现在类定义内部,位置任意(public/protected/private 区域都可) - 被声明为友元的函数不必事先声明,但定义必须在友元声明可见的作用域中(通常放在类外)
- 模板函数作友元时,要小心 ADL(参数依赖查找)失效问题:建议用
friend void f(T);显式特化,而非friend void f(T);
如何正确声明 friend 非成员函数
最常踩的坑是链接错误:undefined reference to 'f(MyClass)'。根本原因是声明和定义不匹配——类内写的 friend void f(MyClass); 是一个新声明,而你在别处定义的 void f(MyClass) 可能被当成另一个重载函数,导致链接失败。
正确做法是先声明函数,再在类内用 friend 关联:
立即学习“C++免费学习笔记(深入)”;
void f(const MyClass&); // 先声明
class MyClass {
int x = 42;
friend void f(const MyClass&); // 再声明为友元
};
然后在 .cpp 文件里定义:
void f(const MyClass& obj) {
std::cout << obj.x; // ✅ 可以访问 private 成员
}
- 如果函数带模板参数,必须用
friend void f<t>(T);</t>或完整特化形式,避免编译器推导歧义 - 不要在头文件里直接定义友元函数体(除非 inline),否则可能违反 ODR(One Definition Rule)
- 友元函数没有
this指针,所有数据都要显式传入
friend 类 vs friend 成员函数:权限范围差异很大
声明 friend class B; 表示整个 B 类的所有成员函数(包括未来新增的)都能访问当前类的私有成员;而 friend void B::f(); 只授权这一个函数——后者更安全,也更容易追踪权限扩散。
典型误用场景:为了省事把整个工具类设为友元,结果后续有人在该工具类里加了个新函数,意外获得了不该有的访问权,且编译器不会警告。
- 优先用
friend void B::f();而非friend class B;,最小权限原则 -
friend class B;不会自动让B的友元也成为当前类的友元(无传递性) - 嵌套类默认不是外围类的友元,哪怕它定义在 private 区域里
友元破坏封装?关键看是否暴露实现细节
真正的问题从来不是“能不能访问”,而是“为什么要访问”。如果你发现大量逻辑必须靠友元才能拿到私有字段,大概率是类接口设计窄了——比如缺少 get_x() 或 serialize_to(std::ostream&) 这类只读/可控输出接口。
友元合理的使用场景其实很窄:流操作符重载(operator)、容器适配器(如 <code>std::vector 对其迭代器的友元授权)、序列化框架的深度访问点。其余多数情况,加个 const 成员函数比开 friend 更清晰、更易维护。
最容易被忽略的一点:友元关系不参与继承。派生类不会自动继承基类的友元,哪怕它复用了基类的私有字段。想让派生类也能被某函数访问,得单独再声明一次。










