C++中友元的实例详解

发布时间 - 2026-01-11 03:10:30    点击率:

C++中友元的实例详解

尽管友元被授予从外部访问类的私有部分的权限,但他们并不与面向对象的编程思想相悖;相反他提高了公共接口的灵活性。

一、友元类

友元声明可以位于公有、私有活保护部分、其所在位置无关紧要

我直接贴出一个摘自< c++ primer plus >的例子来演示 c++ 友元类

其中 Remote 为 Tv的友元类。

Tv.h

#ifndef TV_H_
#define TV_H_

/*一个类 电视 */
class Tv
{
public:
  friend class Remote; //Remote类可以访问Tv Privite 的私有部分
  enum {
    off,on  //开关 
  };
  enum 
  {
    MinVal,MaxVal=20  //音量
  };
  enum {
    Antena,Cable //使用的天线、还是电缆
  };
  enum 
  {
    TV ,DVD  //工作模式
  };

  Tv(int s = off, int mc = 125) :state(s), volume(5), maxchannel(mc),
    channel(5), mode(Cable), input(TV) {}
  void onoff() { state = (state == on) ? off : on; }
  bool ison()const { return state == on; }
  bool volup();  //增大声音
  bool voldown(); //减小声音
  void chanup(); //频道 +
  void chandown();//频道 -
  void set_mode() { mode = (mode == Antena) ? Cable : Antena; }
  void set_input() { input = (input == TV) ? DVD : TV; }
  void settings()const; //显示所有设置


private:
  int state;  // 开或者 关
  int volume; // 音量
  int maxchannel; //最大
  int channel;  //当前频道
  int mode;  // 广播还是 电缆
  int input; //Tv 或者 DVD
};

/*Remote 的定义 (遥控器) */
class Remote {
private :
  int mode; // 控制 TV 或 DVD
public:
  Remote(int m = Tv::TV) :mode(m) {}
  bool volup(Tv & t) { return t.volup(); }
  bool voldown(Tv & t) { return t.voldown(); }
  void onoff(Tv & t) { return t.onoff(); }
  void chanup(Tv & t) { return t.chanup(); }
  void chandown(Tv & t) { return t.chandown(); }
  void set_chan(Tv &t, int c) { t.channel = c; } //访问了Tv的私有成员
  void set_mode(Tv &t) { t.set_mode(); }
  void set_input(Tv &t) { t.set_input(); }
};
#endif // TV_H_

Tv.cpp

#include "stdafx.h"
#include "Tv.h"
#include <iostream>

bool Tv::volup() {
  if (volume < MaxVal) {
    volume++;
    return true;
  }
  else {
    return false;
  }
}

bool Tv::voldown() {
  if (volume > MinVal) {
    volume--;
    return true;
  }
  else {
    return false;
  }
}

void Tv::chanup() {
  if (channel < maxchannel) channel++;
  else channel = 1;
}

void Tv::chandown() {
  if (channel > 1) channel--;
  else channel = maxchannel;
}

void Tv::settings() const {
  using std::cout;
  using std::endl;
  cout << "TV is " << (state == off ? "off" : "on") << endl;
  if (state == on) {
    cout << "Volume setting =" << volume << endl;
    cout << "Channel setting = " << channel << endl;
    cout << "Mode = " << (mode == Antena ? "antenna" : "cable") << endl;
    cout << "Input = " << (input == TV ? "TV" : "DVD") << endl;
  }
}

测试代码:

#include "stdafx.h"
#include "tv.h"
#include <iostream>

int main()
{
  using std::cout;
  Tv s42;
  cout << "Initial settings for 42 \" Tv: \n";
  s42.settings();
  s42.onoff();
  s42.chanup();

  cout << " \n Adjusted settings for 42 \" Tv: \n";
  s42.chanup();
  cout << "\n Adjusted settings for 42 \" Tv: \n";
  s42.settings();

  Remote grey;
  grey.set_chan(s42, 10);
  grey.volup(s42);
  grey.volup(s42);
  cout << " \n s42 \" settings after using remote: \n";
  s42.settings();

  Tv s58(Tv::on);
  s58.set_mode();
  grey.set_chan(s58, 58);
  cout << " \n s58 \" setting: \n";
  s58.settings();

  system("pause");
  return 0;
}

运行结果:

Initial settings for 42 " Tv:
TV is off

 Adjusted settings for 42 " Tv:

 Adjusted settings for 42 " Tv:
TV is on
Volume setting =5
Channel setting = 7
Mode = cable
Input = TV

 s42 " settings after using remote:
TV is on
Volume setting =7
Channel setting = 10
Mode = cable
Input = TV

 s58 " setting:
TV is on
Volume setting =5
Channel setting = 58
Mode = antenna
Input = TV
请按任意键继续. . .

上述代码中将Remote类设置成为了Tv类的友元类,但事实上我们看到:唯一访问Tv的成员的方法是void set_chan(Tv &t, int c) { t.channel = c; } ,因此它是唯一需要友元的方法。因此不必让整个类成为友元,这就引出了我们下面要讲的的友元成员函数。

二、友元成员函数

我们要再Tv中将Remote::set_chan()设置成友元:

clas Tv
{
  friend void Remote::set_chan(Tv & t,int c ) ;
}

然而要使编译器能够处理这条语句,它必须知道Remote的定义。否则,它无法知道Remote是一个类。而 set_chan是这个类的方法。这意味着应将Remote的定义放到Tv的定义前面。Remote的方法提到了Tv对象,而意味着Tv定义应当位于Remote定义之前,避开这种循环依赖的方法是,使用前向声明。
所以应该这样:

class Tv ; //前向声明
class Remote{...}
class Tv {...}

这里还有一个麻烦就是:

Remote 包含了内联代码例如:void onoff(Tv &t) {t.onoff();};

由于这将调用Tv的一个方法,所以编译器此时已经看到了Tv类的声明,这样才能知道Tv有哪些方法,但正如看到的,该声明位于Remote声明的后面。这种问题的解决方法是:使用Remote声明中只包含方法声明,并将实际的定义放到Tv类之后。 所以最终应该这样:

class Tv; //前向声明
class Remote {...} //如要用到Tv 只能是方法声明
class Tv{...}
//接着写Remote的定义

这里通过方法定义中使用 inline关键字,仍然可以使方法称为内联方法
所以程序最终将tv.h改为:

#ifndef TV_H_
#define TV_H_
class Tv; //前向声明
class Remote {

public:
  enum {
    off, on  //开关 
  };
  enum
  {
    MinVal, MaxVal = 20  //音量
  };
  enum {
    Antena, Cable //使用的天线、还是电缆
  };
  enum
  {
    TV, DVD  //工作模式
  };

private:
  int mode; // 控制 TV 或 DVD
public:
  Remote(int m = TV) :mode(m) {}
  //用到了Tv 只能是声明
  bool volup(Tv & t);
  bool voldown(Tv & t);
  void onoff(Tv & t);
  void chanup(Tv & t);
  void chandown(Tv & t);
  void set_chan(Tv &t, int c);
  void set_mode(Tv &t);
  void set_input(Tv &t);
};

class Tv
{
public:
  friend void Remote::set_chan(Tv & t,int c); //友元成员函数
  enum {
    off, on  //开关 
  };
  enum
  {
    MinVal, MaxVal = 20  //音量
  };
  enum {
    Antena, Cable //使用的天线、还是电缆
  };
  enum
  {
    TV, DVD  //工作模式
  };
  Tv(int s = off, int mc = 125) :state(s), volume(5), maxchannel(mc),
    channel(5), mode(Cable), input(TV) {}
  void onoff() { state = (state == on) ? off : on; }
  bool ison()const { return state == on; }
  bool volup();  //增大声音
  bool voldown(); //减小声音
  void chanup(); //频道 +
  void chandown();//频道 -
  void set_mode() { mode = (mode == Antena) ? Cable : Antena; }
  void set_input() { input = (input == TV) ? DVD : TV; }
  void settings()const; //显示所有设置
private:
  int state;  // 开或者 关
  int volume; // 音量
  int maxchannel; //最大
  int channel;  //当前频道
  int mode;  // 广播还是 电缆
  int input; //Tv 或者 DVD
};

inline bool Remote::volup(Tv & t) { return t.volup(); }
inline bool Remote::voldown(Tv & t) { return t.voldown(); }
inline void Remote::onoff(Tv & t) { return t.onoff(); }
inline void Remote::chanup(Tv & t) { return t.chanup(); }
inline void Remote::chandown(Tv & t) { return t.chandown(); }
inline void Remote::set_chan(Tv &t, int c) { t.channel = c; }
inline void Remote::set_mode(Tv &t) { return t.set_mode(); }
inline void Remote::set_input(Tv &t) { return t.set_input(); }
#endif // TV_H_

测试结果不变。

*另外:也可一个将内联函数放在tv.cpp中,但必须去掉inline关键字,这样函数的连接性将成为外部的。

三、其他友元关系

1、上面的代码表示的是Remote是Tv的友元。但我们有时也会用到2个类互相友元。即Remote是Tv的友元,同时 Tv又是Remote的友元

他们定义与下面类似:

class Remote
class Tv
{
friend clas Remote
public:
  void buzz(Remote & r) ;
  ...
}

class Remote
{
friend class Tv;
public:
  void Bool volup(Tv & t){t.volup();}
  ...
}
inline void Tv::buzz(Remote & r)
{
...
}

由于Remote的声明位于Tv声明的后面,所以可以在类的定义Remote::volup(),但Tv::buzz()方法必须在Tv声明的外部定义,使其位于Remote声明的外面。如果不希望buzz()是内联的,则应在一个单独的方法定义文件中定义它。

2、共同的友元。

需要使用友元的另一种情况是,函数需要访问两个类的私有数据。它可以是一个类的友元,同时是另一个类的友元。示例如下:

class Analyzer;
class Probe
{
  friend void sync (Analyzer & a,const Probe & p) ;
  friend void sync (Probe & p,const Analyzer & a);
  ...
};
class Analyzer
{
  friend void sync (Analyzer & a,const Probe & p) ;
  friend void sync (Probe & p,const Analyzer & a);
}
inline void sync (Analyzer & a,const Probe & p)
{
  ...
}
inline void sync (Probe & p,const Analyzer & a)
{
  ...
}

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# C++  # 友元  # 友元的实例详解  # 友元的使用方法  # C++类和对象之封装详解  # C++类和对象之多态详解  # c++类和对象基本概念  # C++的友元和内部类你了解吗  # C++ 类的友元机制解读  # C++的对象特性和友元你真的了解吗  # 前向  # 是一个  # 的是  # 放在  # 也会  # 又是  # 如有  # 也可  # 这就  # 这条  # 希望能  # 并将  # 还有一个  # 使其  # 它可以  # 无关紧要  # 谢谢大家  # 请按  # 解决方法  # 这将 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何在万网自助建站平台快速创建网站?  C语言设计一个闪闪的圣诞树  Android使用GridView实现日历的简单功能  bootstrap日历插件datetimepicker使用方法  java获取注册ip实例  网站制作报价单模板图片,小松挖机官方网站报价?  如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框  Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理  Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】  微信小程序 scroll-view组件实现列表页实例代码  Laravel如何使用Passport实现OAuth2?(完整配置步骤)  javascript中数组(Array)对象和字符串(String)对象的常用方法总结  INTERNET浏览器怎样恢复关闭标签页_INTERNET浏览器标签恢复快捷键与方法【指南】  Android利用动画实现背景逐渐变暗  如何确认建站备案号应放置的具体位置?  详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)  如何获取PHP WAP自助建站系统源码?  动图在线制作网站有哪些,滑动动图图集怎么做?  JS中对数组元素进行增删改移的方法总结  Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】  Windows10如何更改计算机工作组_Win10系统属性修改Workgroup  如何用IIS7快速搭建并优化网站站点?  在线制作视频的网站有哪些,电脑如何制作视频短片?  Win10如何卸载预装Edge扩展_Win10卸载Edge扩展教程【方法】  东莞市网站制作公司有哪些,东莞找工作用什么网站好?  Laravel如何与Pusher实现实时通信?(WebSocket示例)  Laravel怎么集成Vue.js_Laravel Mix配置Vue开发环境  东莞专业网站制作公司有哪些,东莞招聘网站哪个好?  悟空识字如何进行跟读录音_悟空识字开启麦克风权限与录音  奇安信“盘古石”团队突破 iOS 26.1 提权  如何挑选优质建站一级代理提升网站排名?  php8.4header发送头信息失败怎么办_php8.4header函数问题解决【解答】  ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集  美食网站链接制作教程视频,哪个教做美食的网站比较专业点?  Laravel如何使用Livewire构建动态组件?(入门代码)  如何快速辨别茅台真假?关键步骤解析  Android中AutoCompleteTextView自动提示  如何用PHP工具快速搭建高效网站?  如何在阿里云虚拟服务器快速搭建网站?  JavaScript如何实现继承_有哪些常用方法  js实现获取鼠标当前的位置  高防服务器:AI智能防御DDoS攻击与数据安全保障  Python文件操作最佳实践_稳定性说明【指导】  企业网站制作这些问题要关注  Windows Hello人脸识别突然无法使用  Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程  Android 常见的图片加载框架详细介绍  Laravel如何使用模型观察者?(Observer代码示例)  如何在IIS中配置站点IP、端口及主机头?  香港服务器建站指南:外贸独立站搭建与跨境电商配置流程