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

c++ primer plus 第15章友,异常和其他:友元类

c++ primer plus 第15章友,异常和其他:友元类

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:友元类


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

文章目录

  • c++ primer plus 第15章友,异常和其他:友元类
  • 15.1 友元
    • 15.1.1 友元类
    • 程序清单 15.1 tv.h
    • 程序清单 15.2 tv.cpp
  • 总结


15.1 友元

本书前面的一些示例将友元函数用于类的扩展接口中,类并非只能拥有友元函数,也可以将类作为友元。在这种情况下,友元类的所有方法都可以访问原始类的私有成员和保护成员。另外,也可以做更严格的限制,只将特定的成员函数指定为另一个类的友元。哪些函数、成员函数或类为友元是由类定义的,而不能从外部强加友情。因此,尽管友元被授予从外部访问类的私有部分的权限,但它们并不与面向对象的编程思想相悖;相反,它们提高了公有接口的灵活性。

15.1.1 友元类

什么时候希望一个类成为另一个类的友元呢?我们来看一个例子。假定需要编写一个模拟电视机和遥控器的简单程序。决定定义一个Tv类和一个 Remote类,来分别表示电视机和遥控器。很明显,这两个类之间应当存在某种关系,但是什么样的关系呢?遥控器并非电视机,反之亦然,所以公有继承的is-a关系并不适用。遥控器也非电视机的一部分,反之亦然,因此包含或私有继承和保护继承的 has-a关系也不适用。事实上,遥控器可以改变电视机的状态,这表明应将 Romote 类作为Tv类的一个友元。首先定义Tv类。可以用一组状态成员(描述电视各个方面的变量)来表示电视机。下面是一些可能的状态:

  • 开/关;
  • 频道设置;
  • 音量设置;
  • 有线电视或天线调节模式;
  • TV 调谐或 A/V 输入。
  • 调节模式指的是,在美国,对于有线接收和 UHF 广播接收,14频道和14频道以上的频道间隔是不同的。输入选择包括TV(有线TV或广播TV)和 DVD。有些电视机可能提供更多的选择,如多种 DVD/蓝光输入,但对于这个示例的目的而言,这个清单足够了。

另外,电视机还有一些不是状态变量的参数。例如,可接收频道数随电视机而异,可以包括一个记录

这个值的成员。接下来,必须给类提供一些修改这些设置的方法。当前,很多电视机都将控件藏在面板后面,但大多数电视机还是可以在不使用遥控器的情况下进行换台等工作的,通常只能逐频道换台,而不能随意选台。同样,通常还有两个按钮,分别用来增加和降低音量。
遥控器的控制能力应与电视机内置的控制功能相同,它的很多方法都可通过使用Tv方法来实现。另外,遥控器通常都提供随意选择频道的功能,即可以直接从2频道换到20频道,并不用逐次切换频道。另很多遥控器都有多种工作模式,如用作电视控制器和 DVD遥控器。外,这些考虑因素表明,定义应类似于程序清单 15.1。定义中包括一些被定义为枚举的常数。下面的语句使 Remote 成为友元类:
friend class Remote;友元声明可以位于公有、私有或保护部分,其所在的位置无关紧要。由于Remote类提到了Tv类,所以编译器必须了解 Tv 类后,才能处理 Remote 类,为此,最简单的方法是首先定义 Tv类。也可以使用前向声明(forward delaration),这将稍后介绍。

程序清单 15.1 tv.h

// tv.h -- Tv and Remote classes
#ifndef TV_H_
#define TV_H_class Tv
{
public:friend class Remote;   // Remote can access Tv private partsenum {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; // display all settings
private:int state;             // on or offint volume;            // assumed to be digitizedint maxchannel;        // maximum number of channelsint channel;           // current channel settingint mode;              // broadcast or cableint input;             // TV or DVD
};class Remote
{
private:int mode;              // controls TV or 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) { t.onoff(); }void chanup(Tv & t) {t.chanup();}void chandown(Tv & t) {t.chandown();}void set_chan(Tv & t, int c) {t.channel = c;}void set_mode(Tv & t) {t.set_mode();}void set_input(Tv & t) {t.set_input();}
};
#endif

在程序清单15.1中,大多数类方法都被定义为内联的。除构造函数外,所有的Romote方法都将一个Tv 对象引用作为参数,这表明遥控器必须针对特定的电视机。程序清单15.2列出了其余的定义。音量设置函数将音量成员增减一个单位,除非声音到达最大或最小。频道选择函数使用循环方式,最低的频道设置为1,它位于最高的频道设置maxchannel之后。
很多方法都使用条件运算符在两种状态之间切换:

void onoff(){state=(state ==On)? ff :On;

如果两种状态值分别为tnue(1)和false(0),则可以结合使用将在附录E讨论的按位异或和赋值运算符(^=)来简化上述代码:

void onoff()state^=1;

程序清单 15.2 tv.cpp

// tv.cpp -- methods for the Tv class (Remote methods are inline)
#include <iostream>
#include "tv.h"bool Tv::volup()
{if (volume < MaxVal){volume++;return true;}elsereturn false;
}
bool Tv::voldown()
{if (volume > MinVal){volume--;return true;}elsereturn false;
}void Tv::chanup()
{if (channel < maxchannel)channel++;elsechannel = 1;
}void Tv::chandown()
{if (channel > 1)channel--;elsechannel = 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 == Antenna? "antenna" : "cable") << endl;cout << "Input = "<< (input == TV? "TV" : "DVD") << endl;}
}

程序清单 15.3是一个简短的程序,可以测试一些特性。另外,可使用同一个遥控器控制两台不同的电视机。

//use_tv.cpp -- using the Tv and Remote classes
#include <iostream>
#include "tv.h"int main()
{using std::cout;Tv s42;cout << "Initial settings for 42\" TV:\n";s42.settings();s42.onoff();s42.chanup();cout << "\nAdjusted settings for 42\" TV:\n";s42.settings();Remote grey;grey.set_chan(s42, 10);grey.volup(s42);grey.volup(s42);cout << "\n42\" settings after using remote:\n";s42.settings();Tv s58(Tv::On);s58.set_mode();grey.set_chan(s58,28);cout << "\n58\" settings:\n";s58.settings();// std::cin.get();return 0; 
}

下面是程序清单15.1~程序清单15.3组成的程序的输出:
在这里插入图片描述

总结

这个练习的主要目的在于表明,类友元是一种自然用语,用于表示一些关系。如果不使用某些形式的友元关系,则必须将Tv类的私有部分设置为公有的,或者创建一个笨拙的、大型类来包含电视机和遥控器。这种解决方法无法反应这样的事实,即同一个遥控器可用于多台电视机。

相关文章:

  • 003 线程的暂停和中断
  • 接私活儿神器,一款功能强大的 SaaS 快速开发平台
  • 【ubuntu自启shell脚本】——在ubuntu中如何使用系统自带的启动应用程序设置开机自启自己的本地shell脚本
  • (笔记)M1使用hombrew安装qemu
  • Linux应用开发-第三章Linux的目录IO
  • 【Android】构建 Android Automotive OS:适合初学者的指南
  • 一文带你入门机器学习聚类算法
  • Owl 中的 Props 概述
  • 20240705 每日AI必读资讯
  • # windows 安装 mysql 显示 no packages found 解决方法
  • 使用 pyecharts 渲染成图片程序报错: echarts is not defined问题处理
  • SQL面试题练习 —— 求连续段的起始位置和结束位置
  • 51单片机第18步_将TIM0用作13位定时器
  • claude3.5写作——《基于灰色预测的中国人口数量预测》
  • 分页联合查询实现方法(Java)
  • 2017前端实习生面试总结
  • AHK 中 = 和 == 等比较运算符的用法
  • Django 博客开发教程 8 - 博客文章详情页
  • extjs4学习之配置
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • 不上全站https的网站你们就等着被恶心死吧
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 高性能JavaScript阅读简记(三)
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 力扣(LeetCode)21
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • # 数论-逆元
  • $GOPATH/go.mod exists but should not goland
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (2024,Vision-LSTM,ViL,xLSTM,ViT,ViM,双向扫描)xLSTM 作为通用视觉骨干
  • (C)一些题4
  • (rabbitmq的高级特性)消息可靠性
  • (九十四)函数和二维数组
  • (一)SvelteKit教程:hello world
  • (转)机器学习的数学基础(1)--Dirichlet分布
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • .NET C# 使用GDAL读取FileGDB要素类
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?
  • .net开发时的诡异问题,button的onclick事件无效
  • @Autowired @Resource @Qualifier的区别
  • @Autowired和@Resource装配
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [ 云计算 | AWS 实践 ] Java 如何重命名 Amazon S3 中的文件和文件夹
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...
  • [8] CUDA之向量点乘和矩阵乘法
  • [Design Pattern] 工厂方法模式
  • [DevOps云实践] 彻底删除AWS云资源
  • [EWS]查找 文件夹
  • [iOS]-NSTimer与循环引用的理解
  • [Java] 什么是IoC?什么是DI?它们的区别是什么?
  • [java面试]宇信易诚 广州分公司 java笔试题目回忆录
  • [LeeCode]-Divide Two Integers 不用乘除的除法运算
  • [Linux]知识整理(持续更新)
  • [Markdown] 02 简单应用 第二弹