友元类可访问被声明方的private和protected成员(含变量与函数),但不继承、不传递、不双向;需前置声明避免编译错误。

友元类能访问哪些成员
友元类可以访问被声明方的 private 和 protected 成员,包括成员变量和成员函数,但**不自动获得继承权**——也就是说,即使 A 是 B 的友元,B 的派生类 C 并不会因此把 A 当作自己的友元。
常见错误是以为友元关系可传递或继承,结果在子类里调用父类友元类的接口时编译失败,报错类似:‘private_member’ is inaccessible。
- 友元关系是单向的:B 声明 A 为友元,只表示 A 能访问 B,不代表 B 能访问 A
- 友元关系不继承:C : public B 后,A 仍不能访问 C 的
private成员(除非 C 显式再次声明) - 友元关系不可传递:A 是 B 的友元,B 是 C 的友元,A 依然不能访问 C 的私有成员
如何正确定义友元类(含前置声明)
类定义顺序很重要。如果类 A 想声明类 B 为友元,而 B 尚未定义,就必须先做前置声明;否则编译器不认识 B,会报错 'B' does not name a type 或 unknown type name 'B'。
典型写法:
立即学习“C++免费学习笔记(深入)”;
class B; // 前置声明
class A {
friend class B; // 此处允许,因为 B 已声明
private:
int secret = 42;
};
class B {
public:
void peek(const A& a) {
std::cout << a.secret; // OK:访问 A 的 private 成员
}
};
- 前置声明
class B;必须在A定义之前 -
friend class B;必须写在A的内部,且位置不限(public/protected/private 区域内都可) - 不能在类外补加友元声明,比如在
A定义后写friend class B;是无效的
友元类与嵌套类的区别
新手常混淆 friend class X; 和在类内定义 class Inner {};。前者是两个独立类之间的授权关系;后者是作用域嵌套,Inner 默认可访问外围类的 private 成员(无需 friend),但外围类不能反向访问 Inner 的私有部分。
例如:
class Outer {
int x = 100;
class Inner {
public:
void f(Outer& o) { std::cout << o.x; } // OK:嵌套类天然可访问外围 private
};
};
- 嵌套类
Inner不是Outer的友元类,而是其作用域内的类型,访问权限由语言规则隐式赋予 - 若想让
Outer访问Inner的私有成员,必须显式在Inner中声明friend class Outer; - 嵌套类名默认带外围作用域(如
Outer::Inner),而友元类是完全独立的命名空间实体
友元破坏封装?什么情况下该用
友元确实绕过了封装边界,但它不是“坏设计”的代名词——标准库大量使用它,比如 std::string 和其迭代器、std::vector 和 std::allocator 的配合。关键看是否满足:两个类逻辑上高度耦合,且这种耦合属于设计本质,而非临时取巧。
- 适合场景:容器与迭代器、工厂与产品、序列化器与目标类、测试类(如
TestHelper需验证私有状态) - 不适合场景:仅为了少写几个 getter、或因为“懒得重构接口”就加友元
- 替代方案优先考虑:提供受控的
const访问接口(如data() const)、组合优于友元、用 pimpl 模式隔离实现细节
最容易被忽略的是:友元声明一旦加上,就永久开放了全部私有面——哪怕只用到一个字段,也无法限制友元类只能读某几个成员。真要精细控制,得靠接口抽象,而不是依赖 friend。











