【C++】类和对象——流插入和流提取运算符重载

目录

  • 前言
  • ostream和istream
  • 自定义类型的流插入重载
  • 自定义类型的流提取重载
  • 解决私有问题
  • 日期类总接口

请添加图片描述

前言

  我们在上一节实现日期类时,在输入和输出打印时,经常会调用两个函数:

void Insert()//输入函数{cin >> _year;cin >> _month;cin >> _day;if (!CheakDay())cout << "输入错误,请重新输入" << endl;}
void Print()//输出函数{cout << _year << "年" << _month << "月" << _day << "日" << endl;}

  我们经常调用这两个函数进行输入输出日期,我会觉得麻烦,那我可不可以直接使用cout和cin来输出输入呢,这就用到我们流插入和流提取运算符的重载。

ostream和istream

  在cplusplus网站中,就有详细介绍:
在这里插入图片描述
其实,cout是ostream类型的全局对象,cin是一个istream类型的全局对象。这些都是在C++的标准库中写好了,它们被包含在iostream这个头文件里面。
  ostream和istream里面都写了很多函数,想要输入或者输出带精度的都可以进行调用。
  我们都知道,我们可以直接调用cout来输出内置类型,是因为它已经在库中写好了重载函数,如下图所示:
在这里插入图片描述

例如:
int i=1;
cout<<i ——》等价于cout.operator<<(i)
double d=1.1
cout<<d ——》等价于cout.operator<<(d)
cout能自动识别类型,本质上是因为这些流插入重载自动构成函数重载。
cin也一样。

  当我们想要cout一个自定义类型,即cout<<d1,发现代码会报错,因为库里面没有对应写自定义类型的输出,我们要自己重载写一个。

自定义类型的流插入重载

  虽然上图中函数的参数只有一个,但我们要知道的是operator<<是写在ostream这个类里面的,所以这个函数应该是有两个参数,一个是隐藏的this指针,所以实际上库里声明定义的重载应该为:

ostream& operator<<(ostream& this, int val);

  当我们仿照其写自定义类型的流插入重载函数时,ostream& this,这个参数是不能省略的。
  明白了这个,现在我们在日期类中类中声明流插入重载函数:

void operator<<(ostream& out);
//必须用引用传参是因为ostream类型不支持拷贝构造
//(传参时如果传的自定义类型会调用它的拷贝构造)

  在类外定义这个函数时:

void Date::operator<<(ostream& out)
{out << _year << "年" << _month << "月" << _day << "日" << endl;
}

  我们重载的是自定义类型,但自定义类型内部最终还是内置类型。

out << _year << “年” << _month << “月” << _day << “日” << endl;
这一行其实是多个函数的调用,
先执行out<<_year,
它会调用库里的函数:ostream& operator<< (int val);输出
ostream&也就是out作为返回值又变成:out << “年” << _month << “月” << _day << “日” << endl;然后再输出。

到此刻,我们调用cout<<d1时发现会报错,这是为什么呢?

我们将调用void Date::operator<<(ostream& out)这个函数的式子写出来其实是: d1.operator<<(cout);写成这样就可以正常调用。
即d1<<cout。

  其实原因很简单:在运算符重载过程中,参数顺序和操作数顺序必须保持一致。
在这里插入图片描述

我们实际想要写成:cout<<d1,
则参数顺序则应该为:ostream Date
但是存在一个问题,这个函数是Date类中的成员函数,它有隐含一个this指针,把第一个参数占用了,ostream则不能作为第一个参数。此时我们只能将其重载成全局函数。

总结起来就是:operator<<想重载为成员函数,可以,但是使用时d1<<cout不符合习惯,建议重载成全局函数。
如下:

void operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}

在这里插入图片描述
  此时会出现一个私有不可访问的问题,为方便使用,我们先将私有成员变量设为公有。此时可以正常使用,但当我们想连续输出时,如:

cout << d1 << d2;

  此时编译器又会继续报错。因为函数调用会先调用cout << d1,此时没有返回值,所以会报错,我们应该有个返回值,且这个返回值应该是cout,才能使得表达式继续执行,变为cout << d2,就可以连续输出,又因为out是cout的引用,即out是cout的别名,只要返回out即可,所以函数可改为:

ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}

自定义类型的流提取重载

  与自定义类型的流插入重载一致,就不再细说,直接得代码:

//函数的声明
istream & operator>>(istream & in, Date & d);
//函数的定义
istream& operator>>(istream& in, Date& d)
{cout << "请依次输入年月日:" << endl;in >> d._year >> d._month >> d._day;return in;
}

  这时我们也可以将上节课写的日期检查写入:

istream& operator>>(istream& in, Date& d)
{cout << "请依次输入年月日:" << endl;in >> d._year >> d._month >> d._day;if (!d.CheakDay()){cout << "日期非法" << endl;}return in;
}

解决私有问题

  将函数在类中声明为友元函数,这在类和对象终章会讲到。
  代码如下:

class Date
{//声明友元函数friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}
istream& operator>>(istream& in, Date& d)
{cout << "请依次输入年月日:" << endl;in >> d._year >> d._month >> d._day;if (!d.CheakDay()){cout << "日期非法" << endl;}return in;
}

  声明函数是类的朋友,则函数可以访问类中的所有成员,友元的语法就是这么简单。

日期类总接口

  补充了这两个流的重载,我们可以将Date.h完善:

class Date
{friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){if (!CheakDay()){cout << "日期非法" << endl;}}int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int MonthDayArr[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))return 29;elsereturn MonthDayArr[month];}bool CheakDay(){if (_year <= 0 || _month <= 0 || _month > 12 || _day<0 || _day>GetMonthDay(_year, _month))return false;elsereturn true;}bool operator<(const Date& d)const;bool operator<=(const Date& d)const;bool operator>(const Date& d)const;bool operator>=(const Date& d)const;bool operator==(const Date& d)const;bool operator!=(const Date& d)const;Date& operator+=(int n);Date operator+(int n);Date& operator-=(int n);Date operator-(int n);Date& operator++();Date& operator--();Date operator++(int);Date operator--(int);int operator-(const Date& d)const;
private:int _year;int _month;int _day;
};

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/387006.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目比赛经验分享:如何抓住“黄金一分钟”

项目比赛经验分享&#xff1a;如何抓住“黄金一分钟” 前言引起注意&#xff1a;用事实和故事开场明确痛点&#xff1a;描述问题和影响介绍解决方案&#xff1a;简明扼要激发兴趣&#xff1a;使用视觉辅助概述演讲结构&#xff1a;清晰的路线图我的开场白示例结语 前言 在创新的…

(源码分析)springsecurity认证授权

了解 1. 结构总览 SpringSecurity所解决的问题就是安全访问控制&#xff0c;而安全访问控制功能其实就是对所有进入系统的请求进行拦截&#xff0c;校验每个请求是否能够访问它所期望的资源。 根据前边知识的学习&#xff0c;可以通过Filter或AoP等技术来实现&#xff0c;Spr…

鸿蒙应用框架开发【简单时钟】 UI框架

简单时钟 介绍 本示例通过使用ohos.display接口以及Canvas组件来实现一个简单的时钟应用。 效果预览 使用说明 1.界面通过setInterval实现周期性实时刷新时间&#xff0c;使用Canvas绘制时钟&#xff0c;指针旋转角度通过计算得出。 例如&#xff1a;"2 * Math.PI / …

【PHP】系统的登录和注册

一、为什么要学习系统的登录和注册 系统的登录和注册可能存在多种漏洞&#xff0c;这些漏洞可能被恶意攻击者利用&#xff0c;从而对用户的安全和隐私构成威胁。通过学习系统的登录和注册理解整个登录和注册的逻辑方便后续更好站在开发的角度思考问题发现漏洞。以下是一些常见…

BUGKU-WEB-好像需要密码

如果点击start attrack 后出现 Payload set 1: Invalid number settings 的提示&#xff0c;先点hex 后点 decimal 再开始start attrack&#xff0c;这是一个软件bug&#xff0c;需要手动让它刷新。 解题思路 先随便输入测试&#xff1a;admin看看源码吧那就爆破了 据说&…

WEBKIT 通过JavaScript 调用本地,硬件未来之窗OS硬件APP

以酒店为例我们需要调用shen份证读取&#xff0c;采集人脸&#xff0c;门锁写房卡&#xff0c;如何通过浏览器调用 1.通过本地http服务 2.通过webkit模式 这里说政务单位模式的集成 由于篇幅问题&#xff0c;怎么集成webkit就不说了 一、webkkit加载交互本地代码 browser.…

旅游卡,免费,旅游是真的吗?真相是……

但这种包来回大交通&#xff0c;一旦成本大于利润&#xff0c;他们就会以各种理由推卸责任。这就是我在“揭秘&#xff1a;共享旅游卡免费旅游&#xff0c;包来回路费&#xff0c;这背后的3大真相&#xff01;”这篇文章里面讲到那个大妈的惨痛教训。 以上这5点真相&#xff0…

大数据学习之Flink基础(补充)

Flink基础 1、系统时间与事件时间 系统时间&#xff08;处理时间&#xff09; 在Sparksreaming的任务计算时&#xff0c;使用的是系统时间。 假设所用窗口为滚动窗口&#xff0c;大小为5分钟。那么每五分钟&#xff0c;都会对接收的数据进行提交任务. 但是&#xff0c;这里有…

Redis高可用之持久化,以及reids的性能管理

一、redis高可用&#xff1a; 在集群当中有一个非常重要的指标&#xff0c;提供正常服务的时间的百分比&#xff08;365天&#xff09;99.9% redis的高可用含义更加宽泛&#xff0c;正常服务是指标之一&#xff0c;数据容量的扩展&#xff0c;数据的安全性 在redis中实现高可…

平移、旋转、缩放和媒体

一、平移 1.1translate&#xff08;&#xff09;函数 做转换工作可以用translate()函数&#xff0c;这个函数可以改变坐标系。通过改变默认的坐标系&#xff0c;我们可以创建不同的转换方式&#xff0c;包括平移、旋转和缩放。 1.2平移位置案例 案例代码如图1 图1 保存运行如…

Flutter——全网最精致木鱼APP可上架应用市场

研发背景 工作之余&#xff0c;闲来无事&#xff0c;想着研发一款用户可能会经常用到的一款APP,并且能够顺便掌握一下Flutter Material Design 3 UI&#xff0c;所以就有了这款比较精致的木鱼APP的诞生。 开源代码 https://github.com/z244370114/woodenfish

期刊评价指标及其查询方法

1、期刊评价体系一 科睿唯安《期刊引证报告》&#xff08;Journal Citation Reports, JCR&#xff09; 科睿唯安每年发布的《期刊引证报告》&#xff08;Journal Citation Reports, JCR&#xff09;是一个独特的多学科期刊评价工具。JCR数据库提供基于引文数据的统计信息的期…

Linux下文件编译器-GCC/G++

前言 本文介绍了c/c的编译过程以及gcc/g的时使用 一.c/c翻译的本质&#xff1a;将高级语言翻译成二进制 1&#xff09;程序翻译过程&#xff1a; &#xff08;1&#xff09;预处理&#xff08;头文件展开、宏替换、去注释、条件编译&#xff09;还是C语言代码 ​ …

项目:基于gRPC进行项目的微服务架构改造

文章目录 写在前面基本使用封装客户端封装服务端Zookeeper 写在前面 最近学了一下gRPC进行远程调用的原理&#xff0c;所以把这个项目改造成了微服务分布式的架构&#xff0c;今天也是基本实现好了&#xff0c;代码已提交 这里补充一下文档吧&#xff0c;也算记录一下整个过程…

Ruoyi 快速开发平台

Ruoyi 快速开发平台 一、官网二、准备工作2.1 环境要求2.2 必要配置 三、运行系统3.1 后端运行3.2 前端安装及运行 四、自定义开发4.1 新增业务模块4.2 代码生成4.2.1 创建菜单4.2.2 后端代码4.2.3 前端代码 一、官网 链接: 前后端分离版本 回到目录 二、准备工作 2.1 环境要…

【C语言】链式队列的实现

队列基本概念 首先我们要了解什么是队列&#xff0c;队列里面包含什么。 队列是线性表的一种是一种先进先出&#xff08;First In Fi Out&#xff09;的数据结构。在需要排队的场景下有很强的应用性。有数组队列也有链式队列&#xff0c;数组实现的队列时间复杂度太大&#x…

【数据结构】链式二叉树的实现和思路分析及二叉树OJ

【数据结构】链式二叉树的实现和思路分析及二叉树OJ &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;数据结构 文章目录 【数据结构】链式二叉树的实现和思路分析及二叉树OJ前言一.链式二叉树的定义及结构二.链式二叉树的遍历2.1前序遍历2.2中…

Typora 【最新1.8.6】版本安装下载教程 (轻量级 Markdown 编辑器),图文步骤详解,免费领取(软件可激活使用)

文章目录 软件介绍软件下载安装步骤激活步骤 软件介绍 Typora 是一款专为 Markdown 爱好者设计的文本编辑器&#xff0c;它结合了简洁的界面设计与强大的 Markdown 渲染能力&#xff0c;为用户提供了一个流畅、高效的写作环境。以下是对 Typora 更详细的介绍&#xff1a; 核心特…

课程学习前提约束(拓扑排序练习)

很显然的拓扑排序 class Solution { public:bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {int n numCourses;vector<int> record(n1,0);queue<int> q;vector<vector<int>> graph(n 1);for (int i 0; i &l…