Cpp类和对象(中续)(5)

文章目录

  • 前言
  • 一、赋值运算符重载
    • 运算符重载
    • 赋值运算符重载
    • 赋值运算符不可重载为全局函数
    • 前置++和后置++的重载
  • 二、const修饰成员函数
  • 三、取地址及const取地址操作符重载
  • 四、日期类的实现
    • 构造函数
    • 日期 += 天数
    • 日期 + 天数
    • 日期 -= 天数
    • 日期 - 天数
    • 日期类的大小比较
      • 日期类 > 日期类
      • 日期类 == 日期类
      • 日期类 >= 日期类
      • 日期类 < 日期类
      • 日期类 <= 日期类
      • 日期类 != 日期类
    • 日期类 - 日期类
  • 五、流插入流提取运算符重载
  • 总结


前言

  继承上节的内容,本节内容依旧量大管饱!!


一、赋值运算符重载

运算符重载

  C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,其目的就是让自定义类型可以像内置类型一样可以直接使用运算符进行操作

请记住这个目的!我们的接下来的一切都是围绕这个来展开

  运算符重载函数也具有自己的返回值类型,函数名字以及参数列表。其返回值类型和参数列表与普通函数类似。

  运算符重载函数名为:关键字operator后面接需要重载的操作符符号。

注意:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@。(需要是C/C++语法中存在)
  2. 重载操作符必须有一个类类型或枚举类型的操作数。(不能去重载运算符改变内置类型的行为) -> 其实要真那么搞也可以,但是很无聊,没什么实用的
  3. 用于内置类型的操作符,重载后其含义不能改变。-> 例如内置的整型+,不能改变其含义
  4. 作为类成员的重载函数时,函数有一个默认的形参this,限定为第一个形参。
  5. sizeof 、:: 、.* 、?: 、. 这5个运算符不能重载。
  6. 并不是运算符都是需要重载的,需要看是否有存在的意义,参数部分需要对应顺序
// 来个实际例子
class Date
{
public:Date(int year = 0, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}// 注意此时该函数的第一个形参默认为this指针bool operator==(const Date& d)// 运算符重载函数{return _year == d._year&&_month == d._month&&_day == d._day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 9, 22);Date d2 = d1;// 注意优先级cout << (d1 == d2) << endl; // 1cout << d1.operator==(d2) << endl; // 与上一条语句等价return 0;
}

  显然第一种 d1 == d2 这种形式相当明了,就好像Date真的是我们的内置类型一样,其实说白了,还是编译器帮我们化成了第二种形式,不信我们可以看下汇编形式:

在这里插入图片描述

还是那句话,当你觉得轻松的时候,总是有人为你负重前行

赋值运算符重载

 先上代码:

class Date
{
public:Date(int year = 0, int month = 1, int day = 1) // 构造函数{_year = year;_month = month;_day = day;}Date& operator=(const Date& d) // 赋值运算符重载函数{if (this != &d) // 防止会有 d1 = d1 这样自己赋值给自己{_year = d._year;_month = d._month;_day = d._day;}return *this;}void Print()// 打印函数{cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year;int _month;int _day;
};

我们要注意以下几点:

  1. 参数类型设置为引用,并用const进行修饰 -> 由于是自定义类型传参,我们若是使用传值传参,会额外调用一次拷贝构造函数,所以函数的第二个参数最好使用引用传参(第一个参数是默认的this指针,我们管不了)。并且我们也不改变它,那就用 const 来修饰
  2. 函数的返回值使用引用返回 -> 本质上是为了支持连续赋值,所以必须要有个返回值,而且很显然是返回左操作数 d3 = d2 = d1; 所以我们在这里返回 this 指针,且因为此时出了函数作用域this指针指向的对象并没有被销毁,所以可以使用引用返回,又省了拷贝多余的资源消耗
  3. 一个类如果没有显示定义赋值运算符重载,编译器也会自动生成一个,完成对象按字节序的值拷贝 -> 没错,赋值运算符重载编译器也可以自动生成,并且也是支持连续赋值的。但是编译器自动生成的赋值运算符重载完成的是对象按字节序的值拷贝,例如d2 = d1,编译器会将d1所占内存空间的值完完全全地拷贝到d2的内存空间中去,类似于memcpy
// 请注意区分
Date d1(2024,9,22);
Date d2 = d1; // 拷贝构造
Date d3;
d3 = d1; // 赋值// 拷贝构造函数:用一个已经存在的对象去构造初始化另一个即将创建的对象
// 赋值运算符重载函数:在两个对象都已经存在的情况下,将一个对象赋值给另一个对象

赋值运算符不可重载为全局函数

  赋值运算符重载跟拷贝构造类似,如果不显式实现,编译器会生成一个默认的赋值运算符重载,此时用户再类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突,故而赋值运算符只能是类的成员函数(其他运算符函数可以重载为全局函数)

《C++ Primer》第5版P500有言:
在这里插入图片描述

默认生成赋值运算符重载对于内置类型与自定义类型处理方式:

  1. 内置类型成员变量直接赋值的
  2. 自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值

  同样的,我们有个深拷贝和浅拷贝的问题,方式还是跟拷贝构造方式一样,当涉及到动态资源申请的时候深拷贝,否则浅拷贝即可

前置++和后置++的重载

  这两个妙就妙在一样就只有一个操作符,一个操作符在前一个在后

C++给出了它的解决方案:

//++d1
Date& operator++()
{_day += 1;return *this;
}//d1++
Date operator++(int)
{Date temp(*this);_day += 1;return temp;		
}

请注意,后置++重载函数中的参数int没有实际作用,只是为了与前置++构成函数重载,以便区分

二、const修饰成员函数

  将const修饰的 “成员函数” 称之为 const 成员函数,const修饰类成员函数,实际修饰改成员隐含的 this 指针,表明在该成员函数中不能对类的任何成员进行修改,如图:
在这里插入图片描述
在讲解具体的实用场景前,我们还可以先来看看这四个问题,答案和解释我都会相应给出:

  1. const对象可以调用非const成员函数吗?
  2. 非const对象可以调用const成员函数吗?
  3. const成员函数内可以调用其他的非const成员函数吗?
  4. 非const成员函数内可以调用其他的const成员函数吗

答案是:不可以、可以、不可以、可以

解释:
5. 非const成员函数,即成员函数的this指针没有被const所修饰,我们传入一个被const修饰的对象,用没有被const修饰的this指针进行接收,属于权限的放大,函数调用失败
6. const成员函数,即成员函数的this指针被const所修饰,我们传入一个没有被const修饰的对象,用被const修饰的this指针进行接收,属于权限的缩小,函数调用成功
7. 在一个被const所修饰的成员函数中调用其他没有被const所修饰的成员函数,也就是将一个被const修饰的this指针的值赋值给一个没有被const修饰的this指针,属于权限的放大,函数调用失败
8. 在一个没有被const所修饰的成员函数中调用其他被const所修饰的成员函数,也就是将一个没有被const修饰的this指针的值赋值给一个被const修饰的this指针,属于权限的缩小,函数调用成功

权限可以平移,也可以缩小,但是不可以放大

好,我们接下来来看以下代码:

// Print()成员函数没有被const修饰
int main()
{Date d1(2024, 9, 22);d1.Print(); // right,权限平移const Date d2(2024, 9, 22);d2.Print(); // err,权限放大return 0;
}

  但是并非所有函数都需要加上 const 修饰的!如果对成员变量进行读写访问的函数,不能加上 const ,另外,const修饰成员函数是修饰this指针的,那么 流插入>> 与 流提取<< 不是在类中实现,没有隐含的this指针,不能使用 const 修饰

三、取地址及const取地址操作符重载

  这两个默认成员函数一般不用重新定义,编译器会默认生成的,无需你多虑

class Date
{
public:Date* operator&(){return this;}const Date* operator&() const{return this;}
private:int _year; int _month; int _day; 
};

四、日期类的实现

 学完了这6个默认成员函数,我们来写个日期类巩固一下吧:

class Date
{
public:// 构造函数Date(int year = 1900, int month = 1, int day = 1);// 打印函数void Print() const;// 日期+=天数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);// 日期的大小关系比较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;// 日期-日期int operator-(const Date& d) const;// 析构,拷贝构造,赋值重载可以不写,使用默认生成的即可private:int _year;int _month;int _day;
};

构造函数

// 获取某年某月的天数
inline int GetMonthDay(int year, int month)
{// 数组存储平年每个月的天数static int dayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };int day = dayArray[month];if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))){//闰年2月的天数day = 29;}return day;
}
// 构造函数
Date::Date(int year, int month, int day)
{// 检查日期的合法性if (year >= 0&& month >= 1 && month <= 12&& day >= 1 && day <= GetMonthDay(year, month)){_year = year;_month = month;_day = day;}else{// 严格来说抛异常更好cout << "非法日期" << endl;cout << year << "年" << month << "月" << day << "日" << endl;}
}

其中,我们注意一下这个GetMonthDay函数:

  1. 该函数可能被多次调用,所以我们最好将其设置为内联函数
  2. 函数中存储每月天数的数组最好是用static修饰,存储在静态区,避免每次调用该函数都需要重新开辟数组
  3. 逻辑与应该先判断 month == 2 是否为真,因为当不是2月的时候我们不必判断是不是闰年,短路判断

日期 += 天数

  因为出了作用域对象还存在,于是我们用引用返回
逻辑如下:
 1.若日已满,则日减去当前月的天数,月加一。
 2.若月已满,则将年加一,月置为1

// 日期 += 天数
Date& Date::operator+=(int day)
{if (day<0){// 复用operator-=*this -= -day;}else{_day += day;// 日期不合法,通过不断调整,直到最后日期合法为止while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month > 12){_year++;_month = 1;}}}return *this;
}

日期 + 天数

  因为返回对象的值不变,但我们要返回变化后的值,于是就可以通过复用+=、创建临时变量tmp来解决,因为tmp出了作用域就销毁,于是我们用传值返回

// 日期 + 天数
Date Date::operator+(int day) const
{Date tmp(*this);// 拷贝构造tmp,用于返回// 复用operator+=tmp += day;return tmp;
}

日期 -= 天数

逻辑如下:
 1.若日为负数,则月减一。
 2.若月为0,则年减一,月置为12。
 3.日加上当前月的天数

// 日期 -= 天数
Date& Date::operator-=(int day)
{if (day < 0){// 复用operator+=*this += -day;}else{_day -= day;// 日期不合法,通过不断调整,直到最后日期合法为止while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}}return *this;
}

日期 - 天数

 跟日期 + 天数类似,也是复用

// 日期-天数
Date Date::operator-(int day) const
{Date tmp(*this);// 拷贝构造tmp,用于返回// 复用operator-=tmp -= day;return tmp;
}

日期类的大小比较

 这里,我们将充分发挥复用的智慧!

日期类 > 日期类

逻辑:年大则大,月大则大,年月相等比日

bool Date::operator>(const Date& d) const
{if (_year > d._year){return true;}else if (_year == d._year){if (_month > d._month){return true;}else if (_month == d._month){return _day > d._day;}}return false;
}

日期类 == 日期类

逻辑:年月日均相等即为真

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

日期类 >= 日期类

逻辑:复用前面两个

bool Date::operator>=(const Date& d) const
{return *this > d || *this == d;
}

日期类 < 日期类

逻辑:>= 的反面就是 <

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

日期类 <= 日期类

逻辑:> 的反面就是 <=

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

日期类 != 日期类

逻辑:== 的反面就是 !=

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

日期类 - 日期类

 其实也就是算两个日期之差,它还是有点意义的

试想一下,假如你未来结婚,结婚到一半,新娘子问你我们几年几月几日遇见的?
你早有准备,一下就回答上来
突然,新娘子还不死心,问你遇见当天离今天一共经历多少天?
你就可以说:“不急,我先跑个程序”
这就是日期类 - 日期类的一个实际场景运用

有两种思路,请听我细细道来:

 方法一:所谓日期 - 日期,即计算传入的两个日期相差的天数。我们只需要让较小的日期的天数一直加一,直到最后和较大的日期相等即可,这个过程中较小日期所加的总天数便是这两个日期之间差值的绝对值。若是第一个日期大于第二个日期,则返回这个差值的正值,若第一个日期小于第二个日期,则返回这个差值的负值,这很容易,设置一个判别变量 flag 即可

// 日期-日期
int Date::operator-(const Date& d) const
{Date max = *this;// 假设第一个日期较大Date min = d;// 假设第二个日期较小int flag = 1;// 此时结果应该为正值if (*this < d){// 假设错误,更正max = d;min = *this;flag = -1;// 此时结果应该为负值}int n = 0;// 记录所加的总天数while (min != max){min++;// 较小的日期++n++;// 总天数++}return n*flag;
}

 方法二:所谓“年、月、日”,不过也是一种进位的方式,那我们可以全把数堆给“日”这个位,先将年等同,再将月等同,最后直接将两个日期的“日”位相减即可,只需要注意返回的是正值还是负值

int Date::operator-(const Date& d)
{Date greD = (*this) > d ? *this : d;Date smlD = (*this) < d ? *this : d;while (greD._year > smlD._year) {if (isLeapYear(greD._year - 1)) greD._day += 366;else greD._day += 365;greD._year -= 1;}while (greD._month > smlD._month) {greD._month -= 1;greD._day += GetMonthDay(greD._year, greD._month);}while (greD._month < smlD._month) {greD._month += 1;greD._day -= GetMonthDay(greD._year, greD._month);}int ret = greD._day - smlD._day;return *this > d ? ret : -ret;
}

五、流插入流提取运算符重载

  这个比较困难,有一些不懂的概念可以暂且放一下

  其实,我们平时说cout、cin能自动识别类型,本质上就是因为重载,cout属于ostream类,cin属于istream类
在这里插入图片描述
  假如我们 重载运算符<< 为成员函数,隐含的 this 指针占用第一个参数的位置,Date必须是左操作数,使用时候d1<<cout 是不符合我们的习惯的

  于是,我们要将其重载为全局函数,并且把ostream& out放在第一个位置!但是又引出了另一个问题:类外不能访问类中的私有成员,如果将私有权限放开,就缺乏安全性,对此C++中有友元,接下来我们会涉及到,这里就使用下,虽然这个全局函数不在类中,但是可以访问私有成员函数

//友元,告诉该类这两个全局函数是我们的朋友,允许使用私有成员(在类中)friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "" << d._month << "" << d._day << "" << endl;return out;
}istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}

总结

  哈哈,这个中篇终于结束了,想不到吧,学Cpp的第一个大内容就如此困难!
  还有个下篇呢,继续加油吧!

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

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

相关文章

【CSS in Depth 2 精译_036】5.6 Grid 网格布局中与对齐相关的属性 + 5.7本章小结

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

桶排序和计数排序(非比较排序算法)

桶排序 桶排序是一种基于分配的排序算法&#xff0c;特别适合用来排序均匀分布的数据。它的基本思想是将输入的数据分到有限数量的桶里&#xff0c;然后对每个桶内的数据分别进行排序&#xff0c;最后再将各个桶内的数据合并得到最终的排序结果。(通常用于浮点数&#xff0c;因…

Go-知识-定时器

Go-知识-定时器 1. 介绍2. Timer使用场景2.1 设定超时时间2.2 延迟执行某个方法 3. Timer 对外接口3.1 创建定时器3.2 停止定时器3.3 重置定时器3.4 After3.5 AfterFunc 4. Timer 的实现原理4.1 Timer数据结构4.1.1 Timer4.1.2 runtimeTimer 4.2 Timer 实现原理4.2.1 创建Timer…

【Godot4.3】基于状态切换的游戏元素概论

提示 本文的设想性质比较大,只是探讨一种设计思路。完全理论阶段&#xff0c;不可行就当是闹了个笑话O(∩_∩)O哈哈~但很符合我瞎搞的气质。 概述 一些游戏元素&#xff0c;其实是拥有多个状态的。比如一个宝箱&#xff0c;有打开和关闭两个状态。那么只需要设定两个状态的图…

并发编程多线程

1.线程和进程的区别&#xff1f; 进程是正在运行程序的实例&#xff0c;进程中包含了线程&#xff0c;每个线程执行不同的任务不同的进程使用不同的内存空间&#xff0c;在当前进程下的所有线程可以共享内存空间线程更轻量&#xff0c;线程上下文切换成本一般上要比进程上下文…

axure的下载,激活,汉化全过程,多图

1.前言 下载地址&#xff1a;https://pan.baidu.com/s/12xo1mJer2hmBK7QrYM5v-Q?pwd0107#list/path%2Fcsdn%E5%85%B1%E4%BA%AB%E6%96%87%E4%BB%B6 源文章&#xff1a;https://blog.csdn.net/iwanttostudyc/article/details/123773796?ops_request_misc%257B%2522request%25…

STM32 单片机最小系统全解析

STM32 单片机最小系统全解析 本文详细介绍了 STM32 单片机最小系统&#xff0c;包括其各个组成部分及设计要点与注意事项。STM32 最小系统在嵌入式开发中至关重要&#xff0c;由电源、时钟、复位、调试接口和启动电路等组成。 在电源电路方面&#xff0c;采用 3.3V 直流电源供…

.Net网络通信组件 - TouchSocket

文章目录 .Net网络通信组件 - TouchSocket1、新建.Net8控制台项目2、Nuget安装TouchSocket组件3、编写服务端代码4、编写客户端代码5、编写Program代码6、运行效果7、日志组件&#xff08;NLog&#xff09;参考我的另一篇博客 .Net网络通信组件 - TouchSocket 1、新建.Net8控制…

PyCharm的使用

PyCharm的入门使用教程 下载和安装PyCharm&#xff1a; 首先&#xff0c;访问JetBrains官方网站&#xff08;https://www.jetbrains.com/pycharm/&#xff09;下载PyCharm的最新版本。根据您的操作系统选择合适的版本进行下载。 安装完成后&#xff0c;打开PyCharm。 创建新…

深度学习03-神经网络02-激活函数

可以使用这个进行跳转链接​​​​​​​http://playground.tensorflow.org/#activationrelu&batchSize11&datasetspiralDatasetreg-gauss&learningRate0.01ularizationRate0.1&noise0&networkShape7,5,4,3,2&seed0.54477&showTestDatafalse&d…

【Unity设计模式】Unity MVC/MVP架构介绍,及MVC/MVP框架的简单应用

文章目录 什么是MVC&#xff1f;MVC眼花缭乱设计图MVP和MVC最经典的MVC的业务流程Unity MVC 框架示例1. 创建项目结构2. 实现模型3. 实现视图4. 实现控制器5. 使用示例 总结参考完结 什么是MVC&#xff1f; MVC自1982年被设计出来&#xff0c;至今都有着很大比重的使用率&…

HCIA--实验十八:配置全局DCHP

一、实验内容 1.需求/要求&#xff1a; 使用一台5700交换机和一台PC,实现全局DHCP的配置&#xff0c;并且自定义配置网关&#xff0c;配置DNS。 二、实验过程 1.拓扑图&#xff1a; 2.步骤&#xff1a; 1.SW1激活DHCP服务&#xff0c;创建vlan10 2.SW1给vlan10添加ip地址 …

Transformer模型-7- Decoder

概述 Decoder也是N6层堆叠的结构&#xff0c;每层被分3层: 两个注意力层和前馈网络层&#xff0c;同Encoder一样在主层后都加有Add&Norm&#xff0c;负责残差连接和归一化操作。 Encoder与Decoder有三大主要的不同&#xff1a; 第一层 Masked Multi-Head Attention: 采用…

Linux 动静态库

目录 一.静态库 1.理解静态库 a.什么是静态库&#xff1f; b.创建静态库的理论&#xff1f; 2.打包静态库 3.静态库的使用方法 a.头文件找不着 b.链接报错——库函数文件找不着 4.将静态库文件写到系统目录下 a.直接拷贝 b.建立软链接 二.动态库 1.什么是动态库&am…

通过标签实现有序:优化你的 FastAPI 生成的 TypeScript 客户端

在软件开发的世界里&#xff0c;API 客户端代码的质量直接影响着应用程序的性能和可维护性。随着项目规模的扩大&#xff0c;自动化生成的代码往往变得臃肿且难以管理。但幸运的是&#xff0c;通过一系列的优化策略&#xff0c;我们可以显著提升这些代码的优雅与效能。在本文中…

C#如何把写好的类编译成dll文件

1 新建一个类库项目 2 直接改写这个Class1.cs文件 3 记得要添加Windows.Forms引用 4 我直接把在别的项目中做好的cs文件搞到这里来&#xff0c;连文件名也改了&#xff08;FilesDirectory.cs&#xff09;&#xff0c;这里using System.Windows.Forms不会报错&#xff0c;因为前…

用Qt 对接‌百度AI平台

很多同学想利用几大模型AI弄点东西&#xff0c;但又不知道如何去介入&#xff1f;&#xff1f;最近帮同学弄点东西&#xff0c;刚好要接入到AI平台&#xff0c;就顺便研究了一下&#xff0c;并记录下来。 首先我们选择的 AI模型是百度的&#xff0c;然后注册&#xff0c;申请密…

8. 尝试微调LLM大型语言模型,让它会写唐诗

这篇文章与03. 进阶指南&#xff1a;自定义 Prompt 提升大模型解题能力一样&#xff0c;本质上是专注于“用”而非“写”&#xff0c;你可以像之前一样&#xff0c;对整体的流程有了一个了解&#xff0c;尝试调整超参数部分来查看对微调的影响。 这里同样是生成式人工智能导论&…

13年计算机考研408-数据结构

解析&#xff1a; 这个降序链表不影响时间复杂度&#xff0c;因为是链表&#xff0c;所以你想要升序就使用头插法&#xff0c;你想要降序就使用尾插法。 然后我们来分析一下最坏的情况是什么样的。 因为m和n都是两个有序的升序序列。 如果刚好m的最大值小于n的最小值&#xff0…

消息中间件---Kafka

一、什么是Kafka&#xff1f; Kafka是一个分布式流处理平台,类似于消息队列或企业消息传递系统&#xff1b; 流处理事什么呢&#xff1f; 流处理就是数据处理工作流&#xff0c;本质上是一种计算机编程范例。流处理是对接收到的新数据事件的连续处理。‌它涉及对从生产者到消…