友元函数是类设计者主动授予的访问许可,声明必须在类体内任意访问区,格式为friend返回类型函数名(参数列表);,且声明不等于定义。

友元函数不是“绕过封装”的捷径,而是类设计者主动签发的访问许可——用错地方会埋下维护雷,用对了能解耦关键逻辑。
声明位置和语法:friend 必须写在类内部,且不看 public/private
很多人卡在第一步:把 friend 声明写在类外,或者只写在 public 段里以为它必须公开。其实:friend 声明本身没有访问级别限制,可以放在类定义的任意位置(private、protected 或 public 区域都合法),只要在类体内就行。
- 声明格式固定为:
friend 返回类型 函数名(参数列表);,例如friend void print(const MyClass&); - 声明不等于定义——函数体必须在类外实现(除非是内联定义,但极少见)
- 不能在类外补写
friend;否则编译器报错:error: 'xxx' declared as a friend but not defined in the class
为什么 operator
流输出操作符 operator 的左操作数是 <code>std::ostream&,右操作数才是你的类对象。如果你把它写成成员函数,this 指针就绑定在你的类上,导致签名变成 MyClass::operator ——这要求调用形式必须是 <code>obj ,完全反直觉,也违背标准库约定。
- 正确姿势是声明为非成员友元:
friend std::ostream& operator - 这样就能保持自然调用:
std::cout - 它需要访问
obj的私有字段(比如obj.value_),所以必须是友元;普通非成员函数无权读取
参数怎么传:没 this 指针,对象得显式传入
友元函数不是类的一部分,没有隐式 this 指针。所有要访问的类成员,都得通过参数传进来的对象来取。传值、传引用、传指针都可以,但选择直接影响行为和效率:
立即学习“C++免费学习笔记(深入)”;
- 想只读私有成员?用
const MyClass&最安全高效 - 想修改私有成员?必须传非 const 引用或指针,例如
MyClass&或MyClass* - 传值会触发拷贝构造,如果类大或不可拷贝(比如含
std::unique_ptr),直接编译失败 - 常见错误:
void func(MyClass obj)声明后,在函数体内改obj.private_member = 42;——改的是副本,原对象毫发无损
容易被忽略的三个硬约束
友元关系不是“一劳永逸”的授权,它受三重静态规则制约,很多 bug 都源于忽视这些:
-
friend关系单向:A 把 B 声明为友元,B 能访问 A 的私有成员,但 A 不能因此访问 B 的私有成员 - 不继承:基类的友元函数,对派生类的私有成员依然无权访问
- 不传递:A 是 B 的友元,B 是 C 的友元,A 对 C 仍是“陌生人”
- 典型报错:
error: 'xxx' is private within this context,往往就是误以为友元权限能跨类或跨层生效
真正难的不是写对语法,而是在重构时判断:这个函数是否还该保有友元权限?一旦类结构变化(比如拆分、继承、字段封装升级),友元声明很容易变成悬空依赖——它还在那儿,但已不该存在。


















