Skip to content
0

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"