友元是C++中唯一被明确允许打破封装限制的机制,通过主动授权使非成员函数或类访问私有/保护成员;友元函数需在类内用friend声明、类外定义(不加friend),无this指针;友元类的所有成员函数均可访问被授权类的私有/保护成员,但关系不传递、不继承、单向;常用于流操作符重载等需外部函数深度协同的场景。

友元不是类的成员,但它能访问类的私有(private)和保护(protected)成员——这是C++中**唯一被明确允许打破封装限制的机制**,但不破坏类的设计意图,关键在于“主动授权”。
friend函数:外部函数获得类内部访问权
在类内部用friend关键字声明一个普通函数(可以是全局函数,也可以是其他类的成员函数),该函数就成为当前类的友元。它定义在类外,不占用类的对象内存,也不受public/private/protected访问限定符影响。
常见写法:
- 声明时加
friend,定义时**不加**(否则编译报错) - 友元函数参数通常包含类对象的引用或指针,否则无法操作具体实例
- 它没有
this指针,所有访问都需显式通过对象名
示例:
立即学习“C++免费学习笔记(深入)”;
class Box {
private:
double width = 10.5;
friend void printWidth(const Box& b); // 声明为友元
};
void printWidth(const Box& b) { // 定义:不写friend!
std::cout << "Width: " << b.width << '\n'; // ✅ 可直接访问private成员
}
friend类:整个类的所有成员函数都是友元
把一个类声明为另一个类的friend,意味着这个“友元类”的**所有成员函数**(包括后续新增的)都能访问被授权类的私有与保护成员。
注意点:
- 友元关系**不具有传递性**:A是B的友元,B是C的友元,不代表A是C的友元
- 友元关系**不具有继承性**:基类的友元不会自动成为派生类的友元
- 友元关系**是单向的**:声明
friend class B;只让B访问当前类,不代表当前类能访问B的私有成员
典型用途:容器类与迭代器类配合、紧密耦合的辅助类(如std::string和它的私有字符缓冲管理类)。
友元函数作为重载操作符的常用场景
很多二元操作符(如、>>、+、==)需要左操作数是其他类型(比如std::ostream),无法定义为成员函数(否则this会强制占左边)。这时用friend函数最自然。
例如流输出重载:
class Point {
private:
int x, y;
public:
Point(int x=0, int y=0) : x(x), y(y) {}
friend std::ostream& operator<<(std::ostream& os, const Point& p) {
os << "(" << p.x << ", " << p.y << ")"; // ✅ 访问private成员
return os;
}
};
调用std::cout 就能正常工作——因为operator是std::ostream的成员函数,而Point主动授予它访问权限。
使用friend的注意事项和替代思路
friend本质是“可控的破窗”,用不好会削弱封装价值。优先考虑以下替代方式:
- 提供
public的getter/setter接口(适合简单数据访问) - 把逻辑移到类内部,设计更合理的成员函数(推荐)
- 用嵌套类(
class Inner定义在class Outer内部),嵌套类天然可访问外围类的私有成员
只有当外部函数/类**确实需要深度协同且无法合理重构**时,再用friend。例如:两个类共享底层数据结构、序列化工具、调试打印器等。
基本上就这些。friend不是后门,而是接口契约的一部分——你主动签了字,才允许别人进你的房间。










