C++ 中友元函数与友元类的用法详解
在 C++ 的封装设计中,private 成员通常仅限类内访问。本文解析如何利用 friend 关键字创建“好友”关系,通过友元函数(包括普通函数和其他类成员函数)及友元类突破这一限制,实现安全的跨类数据共享。
结果或目标
C++ 默认实行严格的访问控制:public 成员可被外部对象直接调用,而 private 和 protected 成员仅对当前类的成员函数和友元开放。当需要让外部函数或特定类访问私有数据时,需引入 友元(Friend) 机制。友元不是类的成员,但拥有访问该类所有私有和保护成员的权限。
准备条件
- 开发环境:支持 C++ 标准的编译器(如 GCC, Clang, MSVC)。
- 核心概念:理解类的访问修饰符(public/private/protected)。
- 前置知识:了解函数指针与引用的基本用法,以及类的前向声明(Forward Declaration)场景。
真实操作顺序
第1步:将非成员函数声明为友元函数
目的:允许一个不属于任何类的普通全局函数访问某类的私有成员。
入口:在类体内部使用 friend 关键字声明外部函数原型。
动作:
- 定义包含私有成员的目标类。
- 在类中声明一个位于类外的普通函数,并在其前加上
friend。 - 在实际调用该函数时,必须传入对象实例,因为非成员函数没有默认的
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 << " 的年龄是 " << stu.age << ",成绩是 " << stu.score << endl;
}
int main() {
Student stu("小明", 16, 95.5f);
display(stu); // 调用友元函数
return 0;
}
完成状态:程序输出 小明的年龄是 16,成绩是 95.5。若移除类中的 friend 声明,编译器将报错提示私有成员不可访问。
第2步:将其他类的成员函数声明为友元函数
目的:允许 A 类的某个方法访问 B 类的私有数据。
入口:利用 类名::成员函数名 的形式进行声明。
动作:
- 前向声明:如果类 B 的方法需要访问类 A,或者反之,且存在相互依赖,需使用前向声明(如
class Address;)以避免编译错误。 - 声明友元:在提供数据的类(B)中,将接收数据的类(A)的成员函数声明为友元:
friend void A::func(B &obj);。 - 顺序注意:被引用的类的成员必须在类体完整定义后才能被使用(用于解引用或分配内存)。因此,声明顺序通常要求先声明指针/引用,后正式定义类体。
参考代码:
#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 << " 的家庭住址:"
<< addr.province << "省"
<< addr.city << "市"
<< addr.district << "区" << endl;
}
int main() {
Student stu("小明", 16, 95.5f);
Address add("陕西", "西安", "雁塔");
stu.show(add);
return 0;
}
完成状态:正确输出了姓名及完整的地址信息。
第3步:定义和使用友元类
目的:让另一个类的所有成员函数都成为当前类的友元。
入口:使用 friend 类名;。
动作:
- 假设
Class B需要全面访问Class A的内部结构。r /> - 在
Class A中加入声明:friend Class B;。 - 关键限制:
- 单向性:A 是 B 的友元,不代表 B 是 A 的友元。B 的函数不能访问 A 的私有成员,除非 A 也声明 B 为友元。
- 不可传递性:若 B 是 A 的友元,C 是 B 的友元,C 依然无法访问 A 的私有成员。
最佳实践:除非有极特殊的架构需求,否则建议优先使用 友元函数 而非友元类。友元类会赋予整个类广泛的访问权,破坏封装性,增加维护风险。
第4步:掌握类的前向声明(Forward Declaration)
问题:在使用指针或引用时,编译器不需要知道类的完整定义;但在创建对象实例时需要。
解释:
- 可以使用前向声明的场景:声明指向该类的指针变量或引用变量(如
Address* ptr;)。因为这些变量的大小固定(即指针本身的大小),与目标类的大小无关。 - 不可使用场景:直接定义对象(如
Address obj;)。编译器此时需要计算类的大小以分配堆栈空间,必须在看到完整的类定义后才能做到。
完成判断
验证是否正确使用了友元:
- 编译测试:注释掉类中的
friend声明行,尝试访问私有成员,检查是否收到 “'...' is private within this context” 类似的编译错误。 - 逻辑检查:确认友元关系是否符合单向和不可传递的预期,避免无意中扩大暴露面。
简要总结
C++ 的友元机制提供了突破访问控制的灵活性。通过使用 friend 关键字,开发者可以将特定的全局函数、其他类的成员函数或整个类定义为“朋友”。在实际应用中,应谨慎评估是否需要暴露私有数据,尽量缩小友元的范围(优先使用具体的友元函数而非友元类),并正确处理多文件间的类声明顺序。以上就是 C++ 编程中友元函数和友元类的详细内容,更多关于 C++ 面向对象特性的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









