友元提供了一种特殊的机制,可以让一个类允许另一个类或函数访问其私有或受保护的成员。虽然友元增加了便利性,但也会破坏封装性,增加代码耦合度,因此使用时需谨慎。
3.1 友元函数
问题描述:在重载 operator<<
时,无法将其重载为成员函数,因为 cout
作为输出流对象和 this
指针会竞争第一个参数的位置。为了使 cout
成为第一个参数,我们需要将 operator<<
重载为全局函数。但全局函数不能直接访问类的私有成员,这时就需要友元函数来解决这个问题。
友元函数的特点:
- 可以访问类的私有和保护成员,但不属于类的成员函数。
- 不能用
const
修饰。 - 可以在类定义的任何地方声明,不受类访问限定符限制。
- 一个函数可以是多个类的友元函数。
- 友元函数的调用与普通函数的调用相同。
示例代码:
#include <iostream>
using namespace std;
class MyClass {
private:
int value;
public:
MyClass(int v) : value(v) {}
// 声明友元函数
friend ostream& operator<<(ostream& os, const MyClass& obj);
};
// 定义友元函数
ostream& operator<<(ostream& os, const MyClass& obj) {
os << obj.value;
return os;
}
int main() {
MyClass obj(42);
cout << obj << endl; // 输出: 42
return 0;
}
在上面的代码中,operator<<
被定义为友元函数,因此它可以访问 MyClass
类的私有成员 value
。
3.2 友元类
友元类是一种类的所有成员函数都可以访问另一个类的私有和保护成员的机制。
特点:
- 友元关系是单向的,不具有交换性。例如,如果
A
类是B
类的友元,那么B
类可以访问A
类的私有成员,但反过来A
类不能访问B
类的私有成员。 - 友元关系不能传递。如果
B
是A
的友元,而C
是B
的友元,这并不意味着C
是A
的友元。 - 友元关系不能继承。
示例代码:
#include <iostream>
using namespace std;
class Date;
class Time {
private:
int hour;
int minute;
public:
Time(int h, int m) : hour(h), minute(m) {}
// 声明Date类为友元类
friend class Date;
};
class Date {
private:
int day;
int month;
int year;
public:
Date(int d, int m, int y) : day(d), month(m), year(y) {}
void displayTime(const Time& t) {
// 访问Time类的私有成员
cout << "Time: " << t.hour << ":" << t.minute << endl;
}
};
int main() {
Time t(10, 30);
Date d(1, 5, 2023);
d.displayTime(t); // 输出: Time: 10:30
return 0;
}
在上面的代码中,Date
类被声明为 Time
类的友元类,因此 Date
类的成员函数可以访问 Time
类的私有成员。
3.3总结:
- 友元函数和友元类允许访问私有和保护成员,但要谨慎使用,因为这会增加代码的耦合性。
- 友元关系是单向的,不可传递。
- 使用友元可以解决一些特殊情况下的访问权限问题,如重载运算符等。