C++ 中友元函数与友元类的用法详解

沈亦白
沈亦白
发布时间:2026-10-13| 6人浏览过
原创

在 C++ 的封装设计中,private 成员通常仅限类内访问。本文解析如何利用 friend 关键字创建“好友”关系,通过友元函数(包括普通函数和其他类成员函数)及友元类突破这一限制,实现安全的跨类数据共享。

结果或目标

C++ 默认实行严格的访问控制:public 成员可被外部对象直接调用,而 private 和 protected 成员仅对当前类的成员函数和友元开放。当需要让外部函数或特定类访问私有数据时,需引入 友元(Friend) 机制。友元不是类的成员,但拥有访问该类所有私有和保护成员的权限。

准备条件

  • 开发环境:支持 C++ 标准的编译器(如 GCC, Clang, MSVC)。
  • 核心概念:理解类的访问修饰符(public/private/protected)。
  • 前置知识:了解函数指针与引用的基本用法,以及类的前向声明(Forward Declaration)场景。

真实操作顺序

第1步:将非成员函数声明为友元函数

目的:允许一个不属于任何类的普通全局函数访问某类的私有成员。

入口:在类体内部使用 friend 关键字声明外部函数原型。

动作:

  1. 定义包含私有成员的目标类。
  2. 在类中声明一个位于类外的普通函数,并在其前加上 friend。
  3. 在实际调用该函数时,必须传入对象实例,因为非成员函数没有默认的 this 指针。

参考代码:

#include<iostream>
using namespace std;

class Student {
private:
    char *name;
    int age;
    float score;
public:
    // 构造函数
    Student(char *n, int a, float s) : name(n), age(a), score(s) {}
    
    // 声明全局函数 display 为友元
    friend void display(Student &stu);
};

// 全局函数定义(注意不在类作用域内)
void display(Student &stu) {
    // 直接访问私有成员 name, age, score
    cout << stu.name <&lt " 的年龄是 " <&lt stu.age <&lt ",成绩是 " <&lt stu.score <&lt endl;
}

int main() {
    Student stu("小明", 16, 95.5f);
    display(stu); // 调用友元函数
    return 0;
}

完成状态:程序输出 小明的年龄是 16,成绩是 95.5。若移除类中的 friend 声明,编译器将报错提示私有成员不可访问。

第2步:将其他类的成员函数声明为友元函数

目的:允许 A 类的某个方法访问 B 类的私有数据。

入口:利用 类名::成员函数名 的形式进行声明。

动作:

  1. 前向声明:如果类 B 的方法需要访问类 A,或者反之,且存在相互依赖,需使用前向声明(如 class Address;)以避免编译错误。
  2. 声明友元:在提供数据的类(B)中,将接收数据的类(A)的成员函数声明为友元:friend void A::func(B &obj);。
  3. 顺序注意:被引用的类的成员必须在类体完整定义后才能被使用(用于解引用或分配内存)。因此,声明顺序通常要求先声明指针/引用,后正式定义类体。

参考代码:

#include<iostream>
using namespace std;

class Address; // 前向声明

class Student {
private:
    char *name;
    int age;
    float score;
public:
    Student(char *n, int a, float s) : name(n), age(a), score(s) {}
    void show(Address &addr); // 成员函数声明
};

class Address {
private:
    char *province;
    char *city;
    char *district;
public:
    Address(char *p, char *c, char *d) : province(p), city(c), district(d) {}
    // 将 Student 的成员函数显示为友元,使其能访问 Address 的私有成员
    friend void Student::show(Address &addr);
};

void Student::show(Address &addr) {
    cout << name <&lt " 的家庭住址:" 
         <&lt addr.province <&lt "省" 
         <&lt addr.city <&lt "市" 
         <&lt addr.district <&lt "区" <&lt endl;
}

int main() {
    Student stu("小明", 16, 95.5f);
    Address add("陕西", "西安", "雁塔");
    stu.show(add); 
    return 0;
}

完成状态:正确输出了姓名及完整的地址信息。

第3步:定义和使用友元类

目的:让另一个类的所有成员函数都成为当前类的友元。

入口:使用 friend 类名;。

动作:

  1. 假设 Class B 需要全面访问 Class A 的内部结构。r />
  2. 在 Class A 中加入声明:friend Class B;。
  3. 关键限制:
    • 单向性:A 是 B 的友元,不代表 B 是 A 的友元。B 的函数不能访问 A 的私有成员,除非 A 也声明 B 为友元。
    • 不可传递性:若 B 是 A 的友元,C 是 B 的友元,C 依然无法访问 A 的私有成员。

最佳实践:除非有极特殊的架构需求,否则建议优先使用 友元函数 而非友元类。友元类会赋予整个类广泛的访问权,破坏封装性,增加维护风险。

第4步:掌握类的前向声明(Forward Declaration)

问题:在使用指针或引用时,编译器不需要知道类的完整定义;但在创建对象实例时需要。

解释:

  • 可以使用前向声明的场景:声明指向该类的指针变量或引用变量(如 Address* ptr;)。因为这些变量的大小固定(即指针本身的大小),与目标类的大小无关。
  • 不可使用场景:直接定义对象(如 Address obj;)。编译器此时需要计算类的大小以分配堆栈空间,必须在看到完整的类定义后才能做到。

完成判断

验证是否正确使用了友元:

  1. 编译测试:注释掉类中的 friend 声明行,尝试访问私有成员,检查是否收到 “'...' is private within this context” 类似的编译错误。
  2. 逻辑检查:确认友元关系是否符合单向和不可传递的预期,避免无意中扩大暴露面。

简要总结

C++ 的友元机制提供了突破访问控制的灵活性。通过使用 friend 关键字,开发者可以将特定的全局函数、其他类的成员函数或整个类定义为“朋友”。在实际应用中,应谨慎评估是否需要暴露私有数据,尽量缩小友元的范围(优先使用具体的友元函数而非友元类),并正确处理多文件间的类声明顺序。以上就是 C++ 编程中友元函数和友元类的详细内容,更多关于 C++ 面向对象特性的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。