导读:本期聚焦于小伙伴创作的《C++中friend友元函数是什么?如何用它访问类的私有成员》,敬请观看详情。类的私有成员在C++里默认只允许自身方法读写,外部函数直接调用会编译报错。友元函数用friend关键字在类内声明后,就能跳过访问控制拿到私有数据。本文厘清友元不是成员函数这一易混概念,对比普通函数与友元函数的访问差异,给出声明语法与完整示例,说明友元破坏封装的边界及适用场景,帮助你在需要跨函数共享内部状态时写出安全可控的代码。

在C++的封装体系里,类的private成员仅对类自身的成员函数可见,外部普通函数无法直接读取或修改。但有些场景需要让某个外部函数访问内部状态,例如运算符重载或调试输出,这时就可以使用friend友元函数。友元函数并非类的成员,而是在类内部通过friend关键字声明的外部函数,编译器会为其开放对应类的私有访问权限。

C++中friend友元函数是什么?如何用它访问类的私有成员

一、友元函数的基本语法与声明方式

友元函数的核心特征是在类定义内部使用friend关键字进行声明,但该声明并不等于函数定义,也不让函数成为类的成员。被声明为友元的函数可以是全局函数,也可以是其他类的成员函数。编译器在处理访问检查时,会识别friend声明并放行其对private和protected成员的访问。

下面给出一个最基础的示例,展示如何把一个全局函数声明为友元,从而访问私有成员:

#include <iostream>
using namespace std;

class Box {
private:
    int width;
public:
    Box(int w) : width(w) {}
    // 声明外部函数为友元
    friend void printWidth(const Box& b);
};

// 友元函数定义,不是Box的成员
void printWidth(const Box& b) {
    // 直接访问私有成员width
    cout << "Width: " << b.width << endl;
}

int main() {
    Box b(10);
    printWidth(b);
    return 0;
}

从代码可以看出,printWidth并没有写在Box类体内,也不通过对象调用成员运算符,而是直接以普通函数形式接收Box对象引用并读取width。如果没有friend声明,这行读取会触发编译错误,提示私有成员不可访问。

需要注意,friend声明可以放在类的任意访问区段(public、private或protected)中,位置不影响其友元性质。很多初学者误以为放在private里就更隐蔽,实际上只要出现friend关键字,该函数就获得了整个类的访问权。

二、友元函数与成员函数的访问差异

理解友元函数,必须先厘清它和成员函数的本质区别。成员函数自带this指针,作用域属于类,可以访问同类所有对象的私有成员;友元函数没有this,它只是被类“授权”的外部函数,必须通过参数拿到对象才能操作数据。

我们用一张简表对比二者在访问模型上的不同:

对比项成员函数友元函数
是否属于类
是否有this指针
调用方式对象.方法()函数(对象)
访问私有成员直接访问通过参数对象访问

在实践中,如果需要对两个不同类的对象做协同计算,友元往往比增加一堆public getter更简洁。例如一个函数要同时读取类A和类B的私有数据做运算,把它设为两类的友元即可,而不必暴露两者的内部接口。

但也要警惕,友元会打破封装边界。一旦声明,该函数就和类形成强耦合,后续修改私有成员名或结构,友元函数也必须同步改动,否则编译失败。因此友元应仅用于真正需要紧密协作的少数函数,而非随意开放访问。

三、友元函数的典型应用场景

最常见的应用是重载输入输出运算符。以输出运算符<<为例,左操作数是ostream对象,无法改为类的成员,因此只能写成全局函数;但若要输出私有成员,就必须将其声明为类的友元。

示例代码如下:

#include <iostream>
using namespace std;

class Point {
private:
    int x, y;
public:
    Point(int x, int y) : x(x), y(y) {}
    friend ostream& operator<<(ostream& os, const Point& p);
};

ostream& operator<<(ostream& os, const Point& p) {
    os << "(" << p.x << ", " << p.y << ")";
    return os;
}

int main() {
    Point p(3, 4);
    cout << p << endl;
    return 0;
}

这里operator<<作为友元,可以直接拼装x和y,调用者无需关心内部表示。如果不使用友元,就只能提供public的getX和getY,既增加接口面积,也削弱封装意义。

另一个场景是单元测试中的白盒测试函数,测试代码需要核验对象内部状态,将测试函数声明为被测类的友元,可以在不污染产品接口的前提下完成校验。等测试结束,也可以容易地通过条件编译移除友元声明。

四、友元声明的注意事项与误区

很多开发者容易把友元函数和友元类混淆。友元类是指用friend class Other;声明后,Other的所有成员函数都能访问当前类的私有成员,权限范围比单个友元函数更大,引入时应更加谨慎。

此外,友元关系不具备传递性:如果A是B的友元,B是C的友元,A并不能访问C的私有成员。友元也不具备继承性,派生类不会自动获得基类友元的访问权,基类友元也不能访问派生类新增的私有成员。

下面演示一个友元类的简单例子,说明权限放大效果:

#include <iostream>
using namespace std;

class Secret {
private:
    int code;
public:
    Secret(int c) : code(c) {}
    friend class Inspector;
};

class Inspector {
public:
    void show(const Secret& s) {
        cout << "Code: " << s.code << endl;
    }
};

int main() {
    Secret s(42);
    Inspector ins;
    ins.show(s);
    return 0;
}

从示例中可以看到,Inspector的任意方法都能读取Secret的code,这种宽权限在大型项目中可能让封装形同虚设。因此建议优先使用单函数友元,明确最小授权原则,减少不必要的耦合面。

总结来说,C++的friend友元函数是一种受控的访问例外机制,它用显式声明换取了外部函数对私有成员的合法读写能力。只要把握住“非成员、需传参、最小授权”的要点,就能在运算符重载、测试辅助等场景中安全使用,而不会滥用导致架构松散。

c++_friendfriend_functionprivate_member修改时间:2026-08-09 09:15:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。