主题
C++友元
友元类
我的朋友由我自己声明- 友元类的所有方法都可以访问原始类的私有成员和保护成员。
- 友元声明可以位于公有、私有或保护部分,其所在位置无关紧要。
c++
class Tv
{
private:
int state;
int channel;
public:
enum { OFF, ON };
// 声明Remote类是友元类
friend class Remote;
Tv(int s = OFF) : state(s) {}
void onOff() { state = (state == ON) ? OFF : ON; }
}
class Remote
{
public:
void onOff(Tv &t) { t.onOff(); }
void setChannel(Tv &t, int c) { t.channel = c; }
}友元成员函数
可选择仅让特定的类成员称为另一个类的友元,而不必让整个类称为友元,但这样做要非常小心各种声明和定义的顺序,要使用前向声明。上面的代码中只有setChannel方法直接访问了Tv类的成员,因此可以让此方法成为友元成员函数。
c++
class Tv; // 前向声明
class Remote
{
public:
void onOff(Tv &t); // 只能声明,因为此时还不清楚Tv类的成员方法
void setChannel(Tv &t, int c);
};
class Tv
{
private:
int state;
int channel;
public:
friend void Remote::setChannel(Tv &t, int c); // 声明友元的成员函数
Tv(int s = OFF);
void onOff();
}
// 此时编译器已经知道Tv类的成员,可以定义(或在实现文件中定义)
inline void Remote::setChannel(Tv &t, int c){t.channel = c;}c++
#include "tv.hpp"
Tv::Tv(int s) : state(s) {}
void Tv::onOff() { state = (state == ON) ? OFF : ON; }
void Remote::onOff(Tv &t) { t.onOff(); }可以相互声明自己是对方的友元类:
c++
class Tv
{
friend class Remote;
public:
void buzz(Remote &r); // 只能声明,因为此时编译器没有足够的信息
}
class Remote
{
friend class Tv;
public:
void onOff(Tv &t) { t.onOff(); } // 可以定义
}
inline void Tv::buzz(Remote &r) { }友元重载运算符
很多运算符可以使用成员函数或非成员函数来实现运算符重载。一般来说,非成员函数应该是友元函数。
c++
Time operator+(const int hour)
{
Time res = *this;
res.hour = res.hour + hour;
return res;
}c++
friend Time operator+(const int hour, const Time & t); // 声明友元函数
// 定义友元函数,注意不能带friend
Time operator+(const int hour, const Time & t)
{
Time res = t;
res.hour = t.hour + hour;
return res;
}- 对于成员函数,语句
t2 = t1 + 3将转换为t2 = t1.operator+(3); - 对于友元函数,语句
t2 = 3 + t1将转换为t2 = operator+(3, t1)。 对成员函数来说,一个操作数通过this指针隐式传递,另一个操作数作为函数参数显式传递;而对于友元函数来说,两个操作数都作为参数显式传递。
提示
如果将非类的项作为重载运算符函数的第一个操作数,应该使用友元函数。
重载<<运算符时需要注意返回ostream &,以便能继续输出后续的内容:
c++
friend ostream & operator<<(ostream & os, const Time & t);
ostream & operator<<(ostream & os, const Time & t)
{
os << t.hour << ": " << t.minute << ": " << t.second;
return os;
}
Time t1(3, 4, 5);
cout << t1 << " PM\n";
// cout << t1 返回的是 ostream &,所以能继续 cout << " PM"