C++第八节课 日期类的补充

news2024/11/15 12:36:22

在上节课我们提交的代码中,还有着一些不足:

如果我们想要运行下面的函数:

void test4()
{
	Date d1(2023, 5, 5);
	d1 += -50;
	d1.Print();
	Date d2(2023, 5, 5);
	d2 -= -50;
	d2.Print();
}

我们发现之前的代码没有考虑day为负数的情况,可以使用assert断言,但是加上day为负数的情况效果更好!

下面为+=的运算符重载函数:

Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= -day;
	}
	// 方法一
	_day += day;
	while (_day > GetMonthDay(_year,_month))
	{
		_day -= GetMonthDay(_year, _month);
		++_month;
		while (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;
	// 方法二
	// *this = *this + day;
	// return *this
}

下面为-=的运算符重载函数: 

Date& Date::operator-=(int day)
{
	// 考虑day为负数的情况
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day<=0)
	{
		--_month;
		if (_month == 0)
		{
			_month = 12;
			--_year;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

对于++和--也有一些价值和应用场景,python中将++和--删去:

例如这里n--执行10次;--n会执行9次;

对于内置类型来说,前置++和后置++的差别不大;

但是对于自定义类型来说差别非常大,需要自己通过函数重载来区分前置--和后置--,且返回值为对象,会构造拷贝函数!因此使用前置类型效率高!

接下来我们再补充一个函数:求两个日期之间的天数!

这里我们用一种比较巧妙的方法:

例如:要求这两个日期之间的天数,我们可以先比较出两个日期谁大谁小?

然后小的每次++,直到++到和大的数字一样大为止!

// d1 -d2
int Date::operator-(const Date& d)  // 求两个日期之间的天数!
{
	// 假设d1的值为最大值
	Date max = *this;
	Date min = d;
	int flag = 1;  // 此时代码第一个日期大

	if (*this < d)
	{
		Date min = *this;
		Date max = d;
		flag = -1;
	}

	int day = 0;
	while (min!=max)
	{
		++min;
		day++;
	}
	return day * flag;
}

通过返回day*flag巧妙地解决了返回值的正负问题!

补充点:

对于C语言来说,printf只能打印内置类型!无法打印自定义类型!

补充一:流提取函数

C++可以使用cout<<d1;打印自定义类型,但是需要对<<进行运算符重载!

cout<<d1;

 流插入操作符是一个双对象的操作符!(这里一个是日期类对象,还有一个是cout,cout是一个类对象---ostream的类对象!)

ostream是库定义的类!(也就是iostream这个头文件里有ostream类,cout是ostream这个类定义的对象!)

cin是istream这个类定义的对象!

为什么cout能够自动识别变量类型?

实际上就是运算符重载!根据输入参数的类型不同调用不同的重载函数!

运算符重载是为了自定义类型支持运算符!与函数重载没有关系!但是两个运算符重载可以构成函数重载!

总结:

  • 可以直接支持内置类型是因为库里面实现了;
  • 可以直接支持自动识别类型是因为函数重载!

分析下面流输出代码: 

void Date::operator<<(ostream& out)  //对<<实现运算符重载!
{
	//这里共有两个对象操作数;
	// out就是这个cout!还隐含了this指针指向d1;
	out << _year << "年" << _month << "月" << "日" << _day << endl;
}

但是这个代码无法运行!

需要下面格式调用才能实现:

因为上面我们实现的函数中,一共还有两个参数,第一个参数是this,第二个参数为out;

默认第一个参数为左操作数,第二个参数为右操作数;

因此我们调用函数的顺序会反过来!

void test6()
{
	Date d1(2023, 5, 5);
	Date d2(2023, 6, 5);
	//cout << d1;   //d1.operator<<(cout);
	d1 << cout;     //d1.operator<<(cout);
	d1.operator<<(cout);
}

这两种调用形式都可以,但是看着非常奇怪!

问题: 流插入能不能写成成员函数?

答案:不能,因为这里的Date对象默认占用第一个参数,就是做了左操作数!

写出来就一定是下面的形式!不符合我们的使用习惯!

怎么解决?

将该函数写成全局函数,但是类中private修饰的变量我们没变法获取!

此时我们可以采用下面两种方法:

方法一:通过写public类型的函数获取变量,但是这个变量只能读不能修改!

 方法二:使用友元函数

	friend void operator<<(ostream& out,const Date&d);  //对<<实现运算符重载!

在类中声明,然后在全局域实现函数的定义!

此时可以采用两种调用方法:

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

总结

在类中定义:

但是调用习惯与我们不相符!

在全局域定义:

但是需要在类中进行友元函数的声明!

注意点:上面的ostream类型不能+const(流插入的实质就是往cout里面写入数据,如果cout被限制,里面的数据就无法修改,即无法被写入!)

对于连续流插入,不同于以往的连续赋值:

流入的顺序是d2先流入cout中,返回一个cout!然后d3的值再流入cout中!即再打印d3,最后打印d1;

因此,要实现连续的流插入,我们需要对函数体进行修改:

ostream& operator<<(ostream& out, const Date& d)  //对<<实现运算符重载!
{
	//这里共有两个对象操作数;
	// out就是这个cout!还隐含了this指针指向d1;
	out << d._year << "年" << d._month << "月" << "日" << d._day << endl;
	return out;
}

此时即可实现连续的流插入!

友元函数的声明不考虑访问限定符!定义在类中的任何位置都可以!

补充二:流插入函数

同时我们来实现连续的流提取的函数!

流内部参在一些状态机制,我们我们对流进行提取插入等都需要改变流,因此不能使用const限定流的对象!

istream& operator>>(istream& in, Date& d)  //对>>实现运算符重载!
{
	in >> d._year >> d._month >> d._day;
	return in;
}

还有一个注意点:

void test8()
{
	Date d1(2023, 14, 5);
	cout << d1;
}

如果我们调用这里的test8,我们会发现还能正常运行:

14月明显不符合实际,因此我们可以对构造函数进行判断:

Date::Date(int year, int month, int day)
{
	if (month > 0 && month < 13 && day >0 && day <= GetMonthDay(year, month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout<< "非法日期" << endl;
		assert(false);
	}
}

同理,我们对输入>>重定向也需要做改动,防止输入非法日期:

且需要将GetMonthDay这个函数修饰为静态的,只有这样我们才能从类中调用!(详细下节将)

istream& operator>>(istream& in, Date& d)  //对>>实现运算符重载!
{
	int year, month, day;
	in >> year >> month >> day;
	if (month > 0 && month < 13 && day >0 && day <= Date::GetMonthDay(year, month))
	{
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
		cout << "非法日期" << endl;
		assert(false);
	}
	return in;
}

现在我们如果输入的日期不符合格式,就会由于断言直接报错!

问题:为什么这里的d1可以调用Print函数,但是d2加上const修饰后无法调用print函数?

  

        这是因为d1传递的时候就是Date*,d2传递的时候因为前面有const修饰,所以传递的就是const Date*,但是调用成员函数的时候谜面默认参数是Date* this,从const Date*到Date*发生了权限的放大!

怎么解决呢?

只需要向成员函数中传入const Date*this即可!

但是成员函数中的this是隐含的,我们无法修改它!

因此C++规定在成员函数的后面+const表示修饰this指针!

成员函数后面加上const之后,普通对象和const修饰的对象都可以调用!

问题:能不能所有成员函数都加上const?

原则1:不是!要修改成员变量的函数不能+const!

因为this修饰为const,则*this无法修改,this指向的对象内容无法修改!

但是对于d1 + 100,此时d1的值没有被修改,因此运算符重载+函数可以上const来修饰!

还有下面一种情况:

d1为普通的对象;d2为const被修饰的对象;

因此如果是d2<d1,那么d2(d2不能对内容进行修改)传入对应于Date&(默认this可以对内容进行修改),此时出现权限的放大,因此出错!

但是如果bool函数中参数不+const,那么第一个都编译不过去!

因此对于参数中尽量加上const,此时普通对象可以传,修饰的对象也可以传!

原则2:只要成员函数内部不修饰成员变量,都应该加上const进行修饰,这样子const对象和普通对象都可以调用!

最终版的日期类代码:

date.h

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
	friend ostream& operator<<(ostream& out,const Date&d);  //对<<实现运算符重载!
	friend istream& operator>>(istream& in, Date& d);  //对<<实现运算符重载!


public:
	Date(int year = 1, int month = 1, int day = 1);
	void Print()
	{
		cout << _year << "_" << _month << "_" << _day << endl;

	}
	bool operator<(const Date& x)const;
	bool operator==(const Date& x)const;
	bool operator<=(const Date& x)const;
	bool operator>(const Date& x)const;
	bool operator>=(const Date& x)const;
	bool operator!=(const Date& x)const;

	// 日期 + 时间的返回值还是一个日期
	// 实现获取当前月份的天数
	static int GetMonthDay(int year, int month);
	// 实现 日期 + 时间 返回日期
	Date& operator+=(int day);
	Date operator+(int day)const;
	Date& operator-=(int day);
	Date operator-(int day)const;

	Date operator++();  // 前置++
	Date operator++(int);  // 后置++
	Date operator--();  // 前置--
	Date operator--(int);  // 前置++

	int operator-(const Date& d)const;  // 求两个日期之间的天数!
	//int operator-(const Date& d);  // 求两个日期之间的天数!


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

date.cpp

#define _CRT_SECURE_NO_WARNINGS
#include"data.h"

// 分开写拷贝构造函数
Date::Date(int year, int month, int day)
{
	if (month > 0 && month < 13 && day >0 && day <= GetMonthDay(year, month))
	{
		_year = year;
		_month = month;
		_day = day;
	}
	else
	{
		cout<< "非法日期" << endl;
		assert(false);
	}
}
bool Date::operator<(const Date& x)const
{
	if (_year < x._year)
	{
		return true;
	}
	else if (_year == x._year && _month < x._month)
	{
		return true;
	}
	else if (_year == x._year && _month == x._month && _day < x._day)
	{
		return true;
	}
	return false;
}

bool Date::operator==(const Date& x)const
{
	return _year == x._year
		&& _month == x._month
		&& _day == x._day;
}

//   d1<d2
//	 this = d1
//	 x = d2
bool Date::operator<=(const Date& x)const
{
	return *this < x || *this == x;
}

// 大于就是小于等于取反!
bool Date::operator>(const Date& x)const
{
	return !(*this <= x);
}

bool Date::operator>=(const Date& x)const
{
	return !(*this < x);
}

bool Date::operator!=(const Date& x)const
{
	return !(*this == x);
}
int Date::GetMonthDay(int year, int month)
{
	static int daysArr[13] = { 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;
	}
	else
	{
		return daysArr[month];
	}
}
Date& Date::operator+=(int day)
{
	if (day < 0)
	{
		return *this -= -day;
	}
	// 方法一
	_day += day;
	while (_day > GetMonthDay(_year,_month))
	{
		_day -= GetMonthDay(_year, _month);
		++_month;
		while (_month == 13)
		{
			++_year;
			_month = 1;
		}
	}
	return *this;
	// 方法二
	// *this = *this + day;
	// return *this
}

Date Date::operator+(int day)const
{
	// 通过拷贝构造创建一个tmp
	Date tmp(*this);

	// 方法二:复用+=
	tmp += day;   

	// 方法一
	//tmp._day += day;
	//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
	//{
	//	tmp._day -= GetMonthDay(tmp._year, tmp._month);
	//	++tmp._month;
	//	while (tmp._month == 13)
	//	{
	//		++tmp._year;
	//		tmp._month = 1;
	//	}
	//}
	return tmp;
}

Date& Date::operator-=(int day)
{
	// 考虑day为负数的情况
	if (day < 0)
	{
		return *this += -day;
	}
	_day -= day;
	while (_day<=0)
	{
		--_month;
		if (_month == 0)
		{
			_month = 12;
			--_year;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

Date Date::operator-(int day)const
{
	Date tmp = *this;
	tmp -= day;
	return tmp;
	}


Date Date::operator++()   // 前置++
{
	*this += 1;
	return *this;
}

Date Date::operator++(int)  // 后置++
{
	Date tmp = Date(*this);
	*this += 1;
	return tmp;
}

Date Date::operator--()  // 前置--
{
	*this -= 1;
	return *this;
}
Date Date::operator--(int)  // 后置--
{
	Date tmp = Date(*this);
	*this -= 1;
	return tmp;
}

// d1 -d2
int Date::operator-(const Date& d)const  // 求两个日期之间的天数!
{
	// 假设d1的值为最大值
	Date max = *this;
	Date min = d;
	int flag = 1;  // 此时代码第一个日期大

	if (*this < d)
	{
		Date min = *this;
		Date max = d;
		flag = -1;
	}

	int day = 0;
	while (min!=max)
	{
		++min;
		day++;
	}
	return day * flag;
}
// cout<<d1
//void Date::operator<<(ostream& out, const Date& d)  //对<<实现运算符重载!
//{
//	//这里共有两个对象操作数;
//	// out就是这个cout!还隐含了this指针指向d1;
//	out << _year << "年" << _month << "月" << "日" << _day << endl;
//}

ostream& operator<<(ostream& out, const Date& d)  //对<<实现运算符重载!
{
	//这里共有两个对象操作数;
	// out就是这个cout!还隐含了this指针指向d1;
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

istream& operator>>(istream& in, Date& d)  //对>>实现运算符重载!
{
	int year, month, day;
	in >> year >> month >> day;
	if (month > 0 && month < 13 && day >0 && day <= Date::GetMonthDay(year, month))
	{
		d._year = year;
		d._month = month;
		d._day = day;
	}
	else
	{
		cout << "非法日期" << endl;
		assert(false);
	}
	return in;
}

main.cpp

#define _CRT_SECURE_NO_WARNINGS
// 运算符重载有一个自定义类型即可
// 不用全部类型都是自定义类型,例如:日期(自定义类西) + 天数(int)
#include"data.h"
void test1()
{
	Date d1(2023, 11, 20);
	d1 + 100;
	d1.Print();


	//Date d2(d1+100);
	Date d2 = d1 + 100;  // 上面两种调用方法都可以!
	d2.Print();


	d1 += 200;
	d1.Print();

}
void test2()
{
	Date d1(2023, 11, 20);
	++d1;
	d1++;
}

void test3()
{
	Date d1(2023, 5, 5);
	d1 -= 50;
	d1.Print();
}
void test4()
{
	Date d1(2023, 5, 5);
	Date ret = d1++;  // d1.operator++(&d1,0)
	d1 += -50;
	d1.Print();


	Date d2(2023, 5, 5);
	Date ret2 = ++d2; 	// d2.operator++(&d2)
	d2 -= -50;       

	d2.Print();
}
void test5()
{
	Date d1(2023, 5, 5);
	Date d2(2023, 6, 5);
	int a = d2 - d1;
	cout << a << endl;
}
void test6()
{
	Date d1(2023, 5, 5);
	Date d2(2023, 6, 5);
	Date d3(2023, 7, 5);

	//cout << d1;   //d1.operator<<(cout);
	//d1 << cout;     //d1.operator<<(cout);
	//d1.operator<<(cout);
	cout << d1<<d2<<d3;
}

void test7()
{
	Date d1(2023, 5, 5);
	Date d2(2023, 6, 5);
	Date d3(2023, 7, 5);

	//cout << d1;   //d1.operator<<(cout);
	//d1 << cout;     //d1.operator<<(cout);
	//d1.operator<<(cout);
	cin >> d1 >> d2;
	cout << d1 << d2 << d3;
}
void test8()
{
	Date d1(2023, 14, 5);
	cout << d1;
}
void test9()
{
	Date d1(2023, 10, 5);
	cin >> d1;
}
int main()
{
	test9();
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅谈红外测温技术在变电站运维中的应用

0引言 随着市场经济的繁荣发展&#xff0c;社会对电力的需求持续增长。城市供电网络的规模和用电设备的总量也在不断扩大&#xff0c;这导致城市电力系统中潜在的网络安全隐患日益增多。作为电力系统核心组成部分的变压器&#xff0c;其安全、稳定的工作直接关系到电能的质量和…

俄罗斯的Alexey V. Gubin开发的数据恢复文件-零假设恢复只读模式下对扫描/恢复数据起作用-供大家学习研究参考

// 主要特征 // Windows FAT,NTFS,Linux ext2 / 3/4和XFS卷格式化的驱动器或“ RAW文件系统”外部驱动器,USB拇指驱动器和存储卡带有ZAR Data Recovery免费版本的数码照片恢复RAID数据恢复NAS数据恢复MBR损坏数据恢复具有多个逻辑驱动器的分区表恢复支持长文件名和国家文件名…

图神经网络模型应用(8)--2

1.自然语言处理相关的应用 文本分类是自然语言处理中的一个经典应用&#xff0c;图神经网络常用的标准数据集里就包含引用网络中论文的分类&#xff0c;但是作为机器学习领域的通用模型测试数据集&#xff0c;它们并没有充分利用文本本身的结构(每个文档只是用词袋特征来表示),…

【微处理器系统原理和应用设计第十五讲】模拟/数字转换器

一、基础概念 1、概念 ADC将真实世界的例如温度、压力、声音或者图像等的模拟信号转换为更容易储存、处理和发射的数字形式。 2、分类 间接ADC&#xff1a;先将输入模拟电压转化为时间/频率等数字信号&#xff0c;例如双积分型ADC 直接ADC&#xff1a;直接转化为数字量&am…

Pandas的读写数据

目录 读写文件的类型 Excel写 API 准备数据 1.直接写入(默认有索引和标题) 2.写入(去掉索引) 3.写入(去掉索引和标题) 4.写入(去掉索引和标题,指定表单信息) Excel读 API 1.读(默认带有索引和标题) 2.读(指定索引项) 3.读(碰到无标题列和无索引列,指定索引列,标题列…

C++速通LeetCode中等第7题-和为K的子数组(巧用前缀和)

巧用哈希表与前缀和&#xff0c;前缀和差为k的两个序号之间的数组就是满足条件的子数组&#xff0c;用哈希表来存放每个序号的前缀和。 前缀和就是头元素到当前序号子数组元素的和 class Solution { public:int subarraySum(vector<int>& nums, int k) {unordered_…

【软件测试】测试的岗位有哪些?

求职入口有很多&#xff1a;相关企业官网、求职软件、校招、公众号等等。 下面就在某招聘网站上看看测试有哪些岗位吧&#xff01; 测试只是一个统称&#xff0c;在测试下面还有很多细分岗位。 但是测试的岗位主要分为以下俩个方面&#xff1a; 软件测试开发工程师&#xff…

Linux(Centos7)系统下给已有分区进行扩容

本文详细介绍了&#xff0c;如何给Centos中已有分区进行扩容&#xff0c;简单的几条命令即可完成。 文章目录 1. 创建物理卷 (PV)2. 将新的物理卷添加到卷组 (VG)3. 扩展逻辑卷 (LV)4. 扩展文件系统4.1 查看文件系统类型4.2 扩展文件系统 完成 1、首先把vmware中的linux关机&am…

【Python】从基础到进阶(九):探索Python中的迭代器与生成器

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、迭代器1. 什么是迭代器&#xff1f;迭代器的工作流程&#xff1a; 2. 使用内置迭代器3. 自定义迭代器 三、生成器1. 什么是生成器&#xff1f;2. 创建生成器3. 生成器表达式 四、生成器与迭代器的区别五、生成器…

Python基础(六)——PyEcharts数据可视化初级版

案例 【前言&#xff1a;为了巩固之前的Python基础知识&#xff08;一&#xff09;到&#xff08;五&#xff09;&#xff0c;并为后续使用Python作为数据处理的好帮手&#xff0c;我们一起来看几个例子】 使用工具&#xff1a;Echarts Echarts 是一个由百度开源的数据可视化…

Node js介绍

目录 概要**对Node的认识****Node的概念理解****Node和浏览器区别****Node的架构图** **Node的应用场景****Node的安装****安装Node的LTS版本****Node的版本管理工具nvm(了解)** **Node的输入和输出**Node程序传递参数Node的输出 **Node的全局对象****特殊的全局对象****其他的…

C++类与对象深度解析(五):友元机制、内部类与匿名对象的高级应用

文章目录 C类和对象——全面指南前言4. 友元详解4.1 友元的基本概念 4.2 友元函数示例代码&#xff1a;友元函数访问两个类的私有成员输出&#xff1a; 4.3 友元类示例代码&#xff1a;友元类的使用输出&#xff1a; 4.4 友元的特性与限制4.5 友元函数与类的实际应用示例&#…

WAAP解决方案:守护数字时代的安全盾牌

在当今这个数字化、数据驱动的时代&#xff0c;网络安全已成为企业运营中不可或缺的一环。随着Web应用程序和API接口在业务中的广泛应用&#xff0c;其面临的安全威胁也日益复杂多变。为此&#xff0c;WAAP&#xff08;Web Application and API Protection&#xff09;解决方案…

JavaEE:网络编程(UDP)

文章目录 UDPUDP的特点UDP协议端格式校验和前置知识校验和具体是如何工作的? UDP UDP的特点 UDP传输的过程类似于寄信. 无连接: 知道对端的IP和端口号就直接进行传输,不需要建立连接.不可靠: 没有确认机制,没有重传机制,如果因为网络故障导致该段无法到达对方,UDP协议也不会…

字符函数 和 字符串函数 的使用与模拟

前言&#xff1a;在编程的过程中&#xff0c;我们经常要处理字符和字符串&#xff0c;为了⽅便操作字符和字符串&#xff0c;C语⾔标准库中提供了 ⼀系列库函数&#xff0c;接下来我们就学习⼀下这些函数。 目录 1. 字符函数 1.1 字符分类判断函数 1.2 字符转换函数 1.3 练…

Sui主网升级至V1.33.2

其他升级要点如下所示&#xff1a; #19404&#xff1a; 启用对等节点的共识轮次探测。 #19119&#xff1a; 无符号整数现在支持.to_string()方法&#xff0c;例如10u8.to_string()等同于 b”10".to_string()。 GraphQL #18774&#xff1a; 为GraphQL引入.move名称解…

Vue3.0组合式API:依赖注入provide和inject实现跨层组件的通信

1、Prop 逐级透传问题 通常情况下&#xff0c;当我们需要从父组件向子组件传递数据时&#xff0c;会使用 props。想象一下这样的结构&#xff1a;有一些多层级嵌套的组件&#xff0c;形成了一棵巨大的组件树&#xff0c;而某个深层的子组件需要一个较远的祖先组件中的部分数据…

数字源表测试IC芯片电性能方案

芯片测试作为芯片设计、生产、封装、测试流程中的重要步骤&#xff0c;是使用特定仪器&#xff0c;通过对待测器件DUT(DeviceUnderTest)的检测&#xff0c;区别缺陷、验证器件是否符合设计目标、分离器件好坏的过程。其中直流参数测试是检验芯片电性能的重要手段之一&#xff0…

JAVA国际版同城货运新纪元货拉拉货运车系统小程序源码

&#x1f69a;国际货运新风尚&#xff0c;同城搬家轻松享 —— 货拉拉货运车系统&#xff0c;全球互联新体验&#x1f30d; &#x1f310;【开篇&#xff1a;跨越国界的货运新篇章】&#x1f310; 在这个全球化的时代&#xff0c;无论是跨国企业还是个人需求&#xff0c;都渴…

Java通信协议——UDP通信协议,模拟聊天室(完整详解,附有代码)

UDP通信协议&#xff0c;模拟聊天室 需求说明 实现客户咨询问题&#xff0c;客服人员答复问题 分析 咨询时&#xff0c;客户是发送方&#xff0c;客服人员是接收方 答复时&#xff0c;客服人员是发送方&#xff0c;客户是接收方&#xff0c;实现思路和咨询时相同 当客户端输入 …