友元的作用与边界
目录
1. 正常情况:private 外人碰不到
2. 加上 friend:给 foo 发“内部通行证”
3. 也可以给“整个类”发通行证
4. 友元的边界(很容易误会)
5. 为什么要用 friend?(两个典型原因)
场景 1:运算符重载(特别是对称的 operator<<, operator+)
场景 2:两个类需要“互相知道内部细节”
6. 为什么说“不要滥用”?
先别管那些“协作”“耦合”大词,我们用一句大白话先定个性质:
friend= 把“本来进不来的人”,临时拉进自己家里,让 TA 也能碰你的私有成员。
也就是说:
友元 = 特殊通行证,允许别的函数/类访问你类的 private / protected 成员。
1. 正常情况:private 外人碰不到
class A {
private:int x = 42;
};void foo(A& a) {// a.x; // ❌ 编译不过,x 是 private
}
外部函数 foo 正常情况下是没资格访问 A::x 的。
2. 加上 friend:给 foo 发“内部通行证”
class A {friend void foo(A&); // 声明:foo 是我的友元
private:int x = 42;
};void foo(A& a) {// 现在可以直接访问 private 成员了a.x = 100; // ✅ 合法,因为 foo 是 A 的 friend
}
-
friend void foo(A&);的意思就是:“我,
A,允许foo这个函数看到我的 private / protected。”
3. 也可以给“整个类”发通行证
class B; // 前向声明class A {friend class B; // 整个 B 类都是我的朋友
private:int x = 42;
};class B {
public:void change(A& a) {a.x = 10; // ✅ 合法,因为 B 是 A 的 friend}
};
-
friend class B;的意思是:“
B这个类里的所有成员函数,都可以访问A的私有东西。”
4. 友元的边界(很容易误会)
-
友元 ≠ 成员
-
foo虽然能访问A的私有成员,但它依然是“外面”的函数,不是A的成员函数。
-
-
友元关系不传递
-
A 把 B 设为 friend
-
B 再把 C 设为 friend
-
并不代表 C 是 A 的 friend
-
关系只在“谁写了 friend 声明”的那个类里生效
-
-
友元关系不继承
-
A声明friend class B; -
B 只能访问
A的私有 -
A的子类(比如class AChild : public A {})的私有,B 还是不能碰
-
5. 为什么要用 friend?(两个典型原因)
场景 1:运算符重载(特别是对称的 operator<<, operator+)
class Vec2 {friend std::ostream& operator<<(std::ostream& os, const Vec2& v);
public:Vec2(int x, int y) : x(x), y(y) {}
private:int x, y;
};std::ostream& operator<<(std::ostream& os, const Vec2& v) {// 可以访问 v.x, v.y 虽然它们是 privatereturn os << "(" << v.x << ", " << v.y << ")";
}
场景 2:两个类需要“互相知道内部细节”
比如容器和迭代器,迭代器需要访问容器的内部数组指针等,这时用 friend 简化。
6. 为什么说“不要滥用”?
因为:
-
写多了 friend,就等于到处打洞,让很多外部东西都能直接碰你的 private
-
封装性变差,类之间关系变得很紧,代码不好维护
一般建议:
只在确实需要共享内部细节的少数地方用 friend,比如:
-
运算符重载函数
-
明确是“内部实现用”的辅助类/函数(如
Impl,Iterator)
如果一句话再总结:
friend就是:“我信任你,给你看我原本不想给别人看的内部实现”——强大但要少用。
