当前位置: 首页 > news >正文

c++ primer plus 第15章友,异常和其他:15.1.2 友元成员函数

#c++ primer plus 第15章友,异常和其他:15.1.2 友元成员函数
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:15.1.2 友元成员函数


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
    • 15.1.2 友元成员函数
    • 程序清单 15.4 tvfm.h


前言

15.1.2 友元成员函数

15.1.2 友元成员函数

从上一个例子中的代码可知,大多数 Remote 方法都是用Tv类的公有接口实现的。这意味着这些方法不是真正需要作为友元。事实上,唯一直接访问Tv成员的Remote方法是Remote:set_chan(),因此它是唯一需要作为友元的方法。确实可以选择仅让特定的类成员成为另一个类的友元,而不必让整个类成为友元,但这样做稍微有点麻烦,必须小心排列各种声明和定义的顺序。下面介绍其中的原因。让 Remote::set chan()成为 Tv类的友元的方法是,在Tv类声明中将其声明为友元:


class Tv
{
friend void Remote::set chan(Tv &t,int c);
...
}

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

// forward declaration
class Tv;

这样,排列次序应如下:

//forward declar
class Tv ;
class Remote{...} ;
class Tv {...}.

能否像下面这样排列呢?

class Remote;
class Tv{...};
class Remote{...};
//forward declaration

答案是不能。原因在于,在编译器在Tv类的声明中看到 Remote 的一个方法被声明为 Tv 类的友元之前,应该先看到 Remote类的声明和set_chan( )方法的声明。
还有一个麻烦。程序清单15.1的Remote 声明包含了内联代码,例如:

void onoff(Tv &t){t.onoff();}

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

class Tv;//forward declarationclass Remote...;//Tv-using methods as prototypes only
class Tv{...}
//put Remote method definitions here

Remote 方法的原型与下面类似:

void onoff(Tv &t);

检查该原型时,所有的编译器都需要知道Tv是一个类,而前向声明提供了这样的信息。当编译器到达真正的方法定义时,它已经读取了Tv类的声明,并拥有了编译这些方法所需的信息。通过在方法定义中使用 inline 关键字,仍然可以使其成为内联方法。程序清单15.4列出了修订后的头文件。

程序清单 15.4 tvfm.h

// tvfm.h -- Tv and Remote classes using a friend member
#ifndef TVFM_H_
#define TVFM_H_class Tv;                       // forward declarationclass Remote
{
public:enum State{Off, On};enum {MinVal,MaxVal = 20};enum {Antenna, Cable};enum {TV, DVD};
private:int mode;
public:Remote(int m = TV) : mode(m) {}bool volup(Tv & t);         // prototype onlybool voldown(Tv & t);void onoff(Tv & t);void chanup(Tv & t);void chandown(Tv & t);void set_mode(Tv & t);void set_input(Tv & t);void set_chan(Tv & t, int c);
};class Tv
{
public:friend void Remote::set_chan(Tv & t, int c);enum State{Off, On};enum {MinVal,MaxVal = 20};enum {Antenna, Cable};enum {TV, DVD};Tv(int s = Off, int mc = 125) : state(s), volume(5),maxchannel(mc), channel(2), 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 == Antenna)? Cable : Antenna;}void set_input() {input = (input == TV)? DVD : TV;}void settings() const;
private:int state;int volume;int maxchannel;int channel;int mode;int input;
};// Remote methods as inline functions
inline bool Remote::volup(Tv & t) { return t.volup();}
inline bool Remote::voldown(Tv & t) { return t.voldown();}
inline void Remote::onoff(Tv & t) { t.onoff(); }
inline void Remote::chanup(Tv & t) {t.chanup();}
inline void Remote::chandown(Tv & t) {t.chandown();}
inline void Remote::set_mode(Tv & t) {t.set_mode();}
inline void Remote::set_input(Tv & t) {t.set_input();}
inline void Remote::set_chan(Tv & t, int c) {t.channel = c;} 
#endif

如果在 tv.cpp 和 use_tv.cpp 中包含 tvfim.h 而不是 tvh,程序的行为与前一个程序相同,区别在于,只有一个Remote方法是 Tv 类的友元,而在原来的版本中,所有的 Remote 方法都是 Tv 类的友元。图 15.1 说明了这种区别。

在这里插入图片描述
本书前面介绍过,内联函数的链接性是内部的,这意味着函数定义必须在使用函数的文件中。在这个例子中,内联定义位于头文件中,因此在使用函数的文件中包含头文件可确保将定义放在正确的地方。也可以将定义放在实现文件中,但必须删除关键字inline,这样函数的链接性将是外部的。顺便说一句,让整个Remote 类成为友元并不需要前向声明,因为友元语句本身已经指出 Remote 是一个类:

friend class Remote;

相关文章:

  • HTML+CSS笔记
  • ECOLOGY9重置系统管理员密码
  • 【堆 优先队列】23. 合并 K 个升序链表
  • 有哪些在本地运行大模型的方法
  • 交换机需要多大 buffer(续:更一般的原理)
  • 百日筑基第十一天-看看SpringBoot
  • Windows 下用MSYS2 环境为RP2040 编译MicroPython 固件
  • 深度学习基准模型Transformer
  • 开灯问题(数学思路)
  • 第二十条:与抽象类相比,优先选择接口
  • 程序员需要具备的核心竞争力
  • 【等保2.0是什么意思?等保2.0的基本要求有哪些? 】
  • 游戏中的坐标转换函数*2(laya2D)
  • JVM的五大内存区域
  • AGI 之 【Hugging Face】 的【Transformer】的 [ Transformer 架构 ] / [ 编码器 ]的简单整理
  • 【347天】每日项目总结系列085(2018.01.18)
  • angular2 简述
  • canvas 五子棋游戏
  • codis proxy处理流程
  • Fabric架构演变之路
  • Gradle 5.0 正式版发布
  • js中的正则表达式入门
  • Linux Process Manage
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • Python进阶细节
  • Python语法速览与机器学习开发环境搭建
  • Spring Boot MyBatis配置多种数据库
  • Terraform入门 - 1. 安装Terraform
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • 编写符合Python风格的对象
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 两列自适应布局方案整理
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 使用docker-compose进行多节点部署
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 思考 CSS 架构
  • 我的zsh配置, 2019最新方案
  • 我与Jetbrains的这些年
  • 小李飞刀:SQL题目刷起来!
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • 通过调用文摘列表API获取文摘
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • $.ajax()
  • (02)vite环境变量配置
  • (1)无线电失控保护(二)
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第1节 (全局数据、栈和堆)
  • (Python第六天)文件处理
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (windows2012共享文件夹和防火墙设置
  • (超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (佳作)两轮平衡小车(原理图、PCB、程序源码、BOM等)
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (转)大道至简,职场上做人做事做管理