在 C++ 中,友元(Friend) 是一种允许类外的函数或类访问类的私有成员和保护成员的机制。友元关系打破了类的封装性,使得某些特定的函数或类能够直接访问类的内部成员。
友元的种类
C++ 中有几种不同类型的友元,主要包括以下几种:
- 友元函数(Friend Function)
- 友元类(Friend Class)
- 友元成员函数(Friend Member Function)
- 友元模板函数或类(Friend Template)
1. 友元函数(Friend Function)
友元函数 是一个被声明为类的友元的普通函数,它可以访问该类的私有成员和保护成员,尽管该函数并不是类的成员函数。
作用:
- 允许类外的函数访问类的私有成员和保护成员。
- 这种方式用于需要与类紧密交互的非成员函数(例如:全局操作、需要访问私有数据的运算符重载函数等)。
示例:
#include <iostream>
using namespace std;
class MyClass {
private:
int value;
public:
MyClass(int v) : value(v) {}
// 声明友元函数
friend void showValue(const MyClass& obj);
};
// 友元函数实现
void showValue(const MyClass& obj) {
cout << "Value: " << obj.value << endl; // 可以访问私有成员
}
int main() {
MyClass obj(42);
showValue(obj); // 调用友元函数
return 0;
}
输出:
Value: 42
在这个例子中,showValue
是一个友元函数,它可以访问 MyClass
的私有成员 value
。
2. 友元类(Friend Class)
友元类 是一个被声明为类的友元的类。友元类中的所有成员函数都可以访问被声明友元类的私有成员和保护成员。
作用:
- 允许一个类的成员函数访问另一个类的私有和保护成员,通常用于类之间紧密协作的场景。
示例:
#include <iostream>
using namespace std;
class B; // 前向声明
class A {
private:
int valueA;
public:
A(int v) : valueA(v) {}
// 声明 B 为友元类
friend class B;
};
class B {
public:
void showValueA(const A& obj) {//将A的实例化对象传入,通过实例化对象进行访问A的私有成员
cout << "A's value: " << obj.valueA << endl; // 可以访问 A 的私有成员
}
};
int main() {
A a(10);
B b;
b.showValueA(a); // B 的成员函数可以访问 A 的私有成员
return 0;
}
输出:
A's value: 10
在这个例子中,B
是 A
的友元类,因此 B
的成员函数 showValueA
可以访问 A
类的私有成员 valueA
。
3. 友元成员函数(Friend Member Function)
友元成员函数 是一个被声明为某个类的友元的成员函数。与友元函数类似,它可以访问类的私有成员和保护成员,尽管它是另一个类的成员函数。
作用:
- 允许另一个类的成员函数访问该类的私有成员。
示例:
#include <iostream>
using namespace std;
class A {
private:
int value;
public:
A(int v) : value(v) {}
// 声明 B 类的成员函数为友元
friend void B::showValue(const A& obj);
};
class B {
public:
void showValue(const A& obj) {
cout << "A's value: " << obj.value << endl; // 可以访问 A 的私有成员
}
};
int main() {
A a(20);
B b;
b.showValue(a); // B 类的成员函数访问 A 类的私有成员
return 0;
}
输出:
A's value: 20
这里,B::showValue
是 B
类的成员函数,并且被声明为 A
类的友元,因此它可以访问 A
类的私有成员 value
。
4. 友元模板函数或类(Friend Template)
友元模板函数或类 是模板类或模板函数的友元,允许模板类或模板函数访问类的私有成员。它提供了一种灵活的方式来允许特定的模板函数或类访问类的私有成员。
作用:
- 允许模板类或模板函数访问类的私有和保护成员。
示例:
#include <iostream>
using namespace std;
template <typename T>
class MyClass {
private:
T value;
public:
MyClass(T v) : value(v) {}
// 声明友元模板函数
template <typename U>
friend void showValue(const MyClass<U>& obj);
};
// 友元模板函数
template <typename U>
void showValue(const MyClass<U>& obj) {
cout << "Value: " << obj.value << endl;
}
int main() {
MyClass<int> obj(10);
showValue(obj); // 通过友元模板函数访问私有成员
return 0;
}
输出:
Value: 10
在这个例子中,showValue
是一个模板函数,它作为 MyClass
的友元模板函数,可以访问 MyClass
的私有成员。
友元的作用总结
- 打破封装:友元关系允许特定的函数或类访问类的私有成员和保护成员,这有时在实际开发中是必须的,例如操作符重载、复杂的类间协作等场景。
- 提升功能:通过友元函数或友元类,某些外部函数或类可以访问类的私有成员,执行一些通常只有类的成员函数才能执行的操作。
- 控制访问范围:友元关系是受限制的,不是所有函数或类都可以访问类的私有成员,只有被声明为友元的特定函数或类才有权限。
友元的注意事项
- 友元关系不具有继承性:如果类
A
的某个成员函数是友元函数,而B
继承自A
,那么B
的成员函数并不自动成为A
的友元。 - 滥用友元:虽然友元提供了强大的功能,但滥用友元关系可能破坏类的封装性,应谨慎使用。尽量将访问权限限制在合理范围内,避免过度暴露内部实现。
总结
C++ 中的友元可以是:
- 友元函数:允许外部函数访问类的私有成员。
- 友元类:允许另一个类的成员访问类的私有成员。
- 友元成员函数:允许另一个类的成员函数访问类的私有成员。
- 友元模板函数或类:允许模板函数或类访问类的私有成员。
友元主要用于需要与其他函数或类进行紧密协作的场景,允许它们访问类的内部成员。