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

CD21.【C++ Dev】类和对象(12) 流插入运算符的重载

目录

1.要使用的代码

2.知识回顾

3.<<流插入运算符重载

版本1

为什么等价为d1 << cout?

版本2

方法1:修改头文件

方法2:不写在成员函数中,而是在类外写

版本3

版本4:私有情况下的访问(使用公有成员函数)

分析问题

版本5:私有情况下的访问(使用友元函数)(★推荐★)

其他提醒


1.要使用的代码

使用日期类对象:

Date.h:

#pragma once
#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 0, int month = 0, int day = 0);
	Date(const Date& x);
	void Print();

	int _year;
	int _month;
	int _day;
};

Date.cpp:

#include "Date.h"
Date::Date(int year , int month, int day)
{
	//cout <<"构造函数:Date::Date(int year , int month, int day)" << endl;
	_year = year;
	_month = month;
	_day = day;
}

Date::Date(const Date& x)
{
	// << "拷贝构造函数:Date::Date(const Date& x)" << endl;
	_year = x._year;
	_month = x._month;
	_day = x._day;
}

void Date::Print()
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

main.cpp:

#include "Date.h"
int main()
{
	Date d1(2025, 3, 28);
	d1.Print();
	return 0;
}

运行结果:

2.知识回顾

>>和<<

CC2.【C++ Cont】初认识cout,cin和endl

3.<<流插入运算符重载

如果main.cpp中不使用d1.Print(),而想使用cout<<来打印年月日,则需要对<<运算符重载,让它可以支持自定义类型的打印(不能使用printf,只支持内置类型,)

库里面只实现了这些内置类型:

显然是operator<<函数重载了

参考资料 点我跳转

发现:cout为ostream的类对象 


cout << d1;

观察上面这个式子,<<有两个操作数,一个是cout,一个是d1

版本1

可以手动控制格式,如_year._month._day

void Date::operator<<(ostream& out)//其实不能返回void,这个会在版本3中修正
{
	out << _year << "." << _month << "." << _day;
}

测试代码:

#include "Date.h"
int main()
{
	Date d1(2025, 3, 28);
    //本质上调用的式子,调用d1的operator<<成员函数
    //第一个参数是左操作数,第二个参数是右操作数
	d1.operator<<(cout);
	return 0;
}

 运行结果:看起来没有问题

按照之前讲过的文章,d1.operator<<(cout)等价于d1 << cout,但这种写法很奇怪,与平常习惯上写的cout << d1反过来

为什么等价为d1 << cout?

观察函数头:

void Date::operator<<(ostream& out)

其实隐藏了第一个参数this,实际上为

void Date::operator<<(const Date* this, ostream& out)

this指针强制占用了左操作数,导致d1 << cout中d1必须放在左侧

版本2

由上述分析可知:要改cout<<d1,有两个方法:

方法1:修改头文件

此方法不推荐,头文件不建议修改

方法2:不写在成员函数中,而是在类外写

如果在类里面写,this会隐式占用第一个参数,则可以放在类外写

Date.h里添加声明,Date.cpp里写定义

测试代码:

#include "Date.h"
int main()
{
	Date d1(2025, 3, 28);
	cout << d1;
	return 0;
}

运行结果:貌似没有问题

但如果连续使用<<就会出现错误,例如cout << d1 << d1;

下面编译报错:

分析原因:

cout从左向右执行,则cout << d1 << d1转换为void << d1丢失了cout,导致无法写入ostream,观察到cplusplus网上的operator<<的声明,正确的返回值应该是ostream&

版本3

ostream& operator<<(ostream& out, const Date& d)//从返回值类型来看,可以支持连续流插入
{
	out << d._year << "." << d._month << "." << d._day;
	return out;
}

例如:cout<<d2<<d3<<d1→cout<<d3<<d1→cout<<d1

测试代码:

#include "Date.h"
int main()
{
	Date d1(2025, 3, 28);
	cout << d1 << d1;
	return 0;
}

若理解了两个参数的含义和用法,则cout << d1 << d1;的等价写法为

operator<<(operator<<(cout, d1), d1); 

运行结果:

版本4:私有情况下的访问(使用公有成员函数)

如果成员变量_year、_month和_day改成私有,则在类外定义的operator<<是不能访问

创建一个成员函数取得_year、_month和_day的值然后传参给operator<<

Date.cpp:

ostream& operator<<(ostream& out, const Date& d)
{
	out << d.GetYear() << "." << d.GetMonth() << "." << d.GetDay();
	return out;
}

Date.h:

#pragma once
#include <iostream>
using namespace std;
class Date
{
public:
    //省略其他成员函数
	int GetYear()
	{
		return _year;
	}

	int GetMonth()
	{
		return _month;
	}

	int GetDay()
	{
		return _day;
	}

private:
	int _year;
	int _month;
	int _day;
};

main.cpp:

#include "Date.h"
int main()
{
	Date d1(2025, 3, 28);
	cout << d1;
	return 0;
}

但这样写会报错:

发现报错报的都是"不能将“this”指针从“const Date”转换为“Date &”"

分析问题

看一下传参过程:

(权限不能放大)

问题:隐含的this参数的默认类型为Date*,而d.GetYear()、d.GetMonth()和d.GetDay()传的类型是const Data&,要确保this指向的参数不能被修改,只需要在成员函数的后面加上const来限制this

int GetYear() const
{
	return _year;
}

int GetMonth() const
{
	return _month;
}

int GetDay() const
{
	return _day;
}

 重新测试代码,运行结果:正常打印:

版本5:私有情况下的访问(使用友元函数)(★推荐★)

友元函数

除了版本4之外,还要另外一种写法:使用友元函数,其特点为:不受访问限定符限制,突破封装的限制

(这里先简单了解用法,后面的文章会详细讲解)

Date.cpp的operator<<改成

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

Date.h中在class Date里面的任意位置(因为只是一个声明)添加友元函数声明

例如:

class Date
{
	friend ostream& operator<<(ostream& out, const Date& d);
public:
    //省略成员函数
private:
	int _year;
	int _month;
	int _day;
};

或者这样写:

class Date
{
public:
    friend ostream& operator<<(ostream& out, const Date& d);
    //省略成员函数
private:
	int _year;
	int _month;
	int _day;
};

或者这样写:

class Date
{
public:
    //省略成员函数
private:
    friend ostream& operator<<(ostream& out, const Date& d);
	int _year;
	int _month;
	int _day;
};

测试代码:

#include "Date.h"
int main()
{
	Date d1(2025, 3, 28);
	cout << d1;
	return 0;
}

运行结果:

其他提醒

operator<<(ostream& out, const Date& d)的第一个参数不能用const修饰,因为向流中插入东西,需要改变内容,不可用const修饰

流提取运算符参见下篇

相关文章:

  • 埃文科技企业AI大模型一体机——昇腾体系+DeepSeek+RAG一站式解决方案
  • 附录:LInux编辑器学习笔记
  • 技术长期主义:用本分思维重构JavaScript逆向知识体系(一)Babel、AST、ES6+、ES5、浏览器环境、Node.js环境的关系和处理流程
  • Docker学习--本地镜像管理相关命令--docker build 命令
  • IP(Internet Protocol,互联网协议)
  • 解决 CANoe 多测试用例下固定 IP 地址冲突问题的分析与方案
  • 【无标题】Scala函数基础
  • Docker学习--本地镜像管理相关命令--docker images 命令
  • 新能源汽车空调系统(R134A)性能评估(一)
  • 控制大型语言模型(LLM)行为的八种技术
  • 学习笔记--(6)
  • 数据结构(4)——带哨兵位循环双向链表
  • k8s 1.30 部署crondns
  • 进程地址空间:操作系统中的虚拟世界与心灵映射,深入解析进程地址空间
  • 【Axure元件分享】年份范围选择器
  • 批量删除 txt/html/json/xml/csv 等文本文件空白行
  • Spring MVC 页面跳转方案与区别
  • 第十四届蓝桥杯大赛软件赛省赛Python 大学 C 组:6.棋盘
  • 基于 Fluent-Bit 和 Fluentd 的分布式日志采集与处理方案
  • 【零基础入门unity游戏开发——2D篇】SpriteMask精灵遮罩组件
  • 游戏论|暴君无道,吊民伐罪——《苏丹的游戏》中的政治
  • 云南省安委会办公室:大理州安全生产形势比较严峻,事故总量一直居高不下
  • 胳膊一抬就疼,炒菜都成问题?警惕这种“炎症”找上门
  • 中华人民共和国和俄罗斯联邦关于进一步加强合作维护国际法权威的联合声明
  • 首批证券公司科创债来了!拟发行规模超160亿元
  • 澎湃研究所“营商环境研究伙伴计划”启动