【C++初阶】八、初识模板(泛型编程、函数模板、类模板)

news2025/1/11 0:20:43

=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

【C++初阶】七、内存管理
(C/C++内存分布、C++内存管理方式、operator new / delete 函数、定位new表达式)
-CSDN博客

 =========================================================================

                     

目录

             

一 . 泛型编程


二 . 函数模板

函数模板的概念

函数模板的格式

函数模板的原理

函数模板的实例化

隐式实例化:

显式实例化:

模板参数的匹配原则


三 . 类模板

类模板的定义格式

类模板的实例化

图示 --  以栈类为例:


本篇博客相关代码

Test.cpp文件 -- C++文件

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

一 . 泛型编程

                 

  • 我们以前写的函数一般都是针对某种类型实现两值交换Swap函数

    如果交换的两值int类型那就要将Swap函数参数设置为int类型

    如果交换的两值double类型那就要将Swap函数参数设置为double类型……
    通过函数重载实现
                      

  • 对于函数虽然函数重载可以实现函数参数多类型的问题
    但也有一些不好的地方
    1、重载的函数仅仅是类型不同而已具体实现实现逻辑都是很类似
    当接收的函数参数类型不同就需要用户自己增加对应的重载函数
    2、代码可维护性比较其中一个重载函数出错可能所有的重载函数都会出错
                           

  • 那能不能实现一个通用的Swap函数实现泛型编程
    泛型编程 -- 编写与类型无关的通用代码代码复用的一种手段
    C++中为解决这个问题有了模板的概念模板泛型编程的基础
                      
  • 有了模板,相当于告诉编译器一个模子
    编译器能够根据不同的类型利用该模子生成对应类型的代码

    模板分为函数模板类模板
图示:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

二 . 函数模板

函数模板的概念

                   

函数模板代表了一个函数家族该函数模板与类型无关

在使用时被参数化根据实参类型产生函数的特定类型版本

                     

                     


                    

函数模板的格式

              

  • 注意
    typename是用来定义模板参数的关键字上面的T1T2Tn就是模板参数),
    除了可以使用typename来定义还可以使用class来定义

    //函数模板格式:
    template<typename T1, typename T2, ……, typename Tn>
    函数返回值类型 函数名(参数列表)
    {
        // 函数体
    }
图示:

                     

                     


                    

函数模板的原理

            

  • 函数模板是一个蓝图它本身并不是函数
    编译器使用后能产生特定具体类型函数摸具
    所以模板就是将本来应该由我们完成的重复的事情交给了编译器完成
                   
  • 编译器编译阶段对于函数模板的使用
    编译器需要根据传入的实参类型推演生成对应类型的函数以供调用
    比如
    当使用double类型调用函数模板编译器通过对实参类型推演
    模板参数T确定为double类型然后产生一份专门处理double类型的代码
图示:

                     

                     


                    

函数模板的实例化

                   

不同类型的参数调用模板称为函数模板的实例化
模板参数实例化分为隐式示例化显式实例化
               

                   

隐式实例化:

                       

  • 编译器根据实参推演模板参数的实际类型
    上面的图示中的模板参数实例化都是隐式实例化
图示:

                   

  • 隐式实例化
    如果只设置了一个模板参数实参中却有多种类型这时将不能通过编译
    此时有两种处理方式
    1、用户自己来强制转化2、使用显式实例化
图示:

                     

                       

---------------------------------------------------------------------------------------------

                  

显式实例化:

               

  • 不通过模板参数推演识别出实参的类型而是自己显式设置模板参数的类型
    函数名后的<>指定模板参数的实际类型即可
                   
  • 显式实例化如果类型不匹配编译器会尝试进行隐式类型转换
    如果无法转换成功编译器将会报错
                       
  • 显式实例化真正用法
    设置了一个模板函数参数中并没有设置模板参数
    函数体中却使用了模板参数类型或者返回值模板参数类型
    这种情况就需要显式实例化确定实参类型
图示:

                     

                     


                    

模板参数的匹配原则

                     

  • 一个非模板函数可以和一个同名的函数模板同时存在
    而且该函数模板还可以被实例化为这个非模板函数
图示:

                

  • 对于非模板函数同名函数模板如果其它条件都相同
    在调动时会优先调用非模板函数不会从该模板产生出一个示例
    如果模板可以产生一个具有更好匹配的函数那么将选择模板
图示:

                   

  • 模板函数不允许自动类型转换普通函数可以进行自动类型转换

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

三 . 类模板

类模板的定义格式

                 

  • 类模板定义函数模板定义类似定义时将函数的位置换成类即可
    模板参数类型类中定义成员类型时进行使用

    //类模板定义格式:
    template<class T1, class T2, ……, class Tn>
    class 类模板名
    {    
        // 类内成员定义    
    }

                     


                    

类模板的实例化

                

  • 类模板实例化函数模板实例化不同类模板实例化需要在类模板名字后跟<>
    然后将实例化的类型放在<>即可
                        
  • 类模板名字不是真正的类名显式实例化后的结果才是真正的类名
              
  • 同一个类模板显式实例化出的不同类这些类的类型不一样
    栈类模板为例
    Stack<int> st1 Stack<double> st2
    st1 的类型是 Stack<int> ,是用于存储int类型数据
    st2 的类型是 Stack<double> ,是用于存储double类型数据
    st1 st2 类型不一样
图示 --  以栈类为例:

            

  • 注意
    类模板成员函数的声明和实现分离不能分离到两个文件中
    分离时通常都写在一个.h文件
    而且分离后的成员函数实现部分需要设置对应的函数模板
    分离后不指定成员函数的类域而是指定其类模板类型
图示:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

本篇博客相关代码

Test.cpp文件 -- C++文件:

#define _CRT_SECURE_NO_WARNINGS 1

//包含IO流:
#include <iostream>;
//完全展开std命名空间:
using namespace std;


//Swap函数 -- 交换两个int类型数据:
void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}

//Swap函数 -- 交换两个double类型数据:
void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}

//Swap函数 -- 交换两个char类型数据:
void Swap(char& left, char& right)
{
	char temp = left;
	left = right;
	right = temp;
}

/*
* 这里实现了三个Swap函数,
* 分别交换了三种不同的类型,
* 但实现的逻辑都是相同的,就只有交换类型不同,
* 所以就造成了某种程度的“冗余”
* 
* 上面的函数都需要针对具体的类型,
* 那能不能让一个代码能够针对广泛的类型呢,
* C++中就有了泛型编程:
*/

//函数模板:
template<typename T>
//tyename 也可以写成 class
//template<class T> 
//Swap函数 -- 交换两类型数据(泛型编程):
void Swap(T& left, T& right)
{
	char temp = left;
	left = right;
	right = temp;
}
/*
* 使用函数模板即可实现泛型编程,
* 让函数能够针对广泛的类型,
* 而不只能针对一种类型,
* 
* 通过关键字template即可定义一个模板,
* Swap函数的参数设置为模板参数
*/


//主函数:
int main()
{
	int a = 0; //int类型变量
	int b = 1; //int类型变量

	double c = 1.1; //double类型变量
	double d = 2.2; //double类型变量

	//调用设置了模板参数的Swap函数:
	Swap(a, b); //int类型 -- 模板参数T
	Swap(c, d); //double类型 -- 模板参数T

	/*
	* 这里调用的两个Swap函数实际不是同一个,
	* 两个Swap函数的函数地址不同,
	* 不同类型调用的Swap函数不同是由模板参数导致的
	* 
	*			模板的原理:
	* 模板参数接受参数如果是int类型,
	* 需要调用到int类型的函数,
	* T 模板参数就会推演成 int类型,(模板参数推演)
	* 然后就会实例化出具体的函数:
	* T 是int类型的对应函数。(模板实例化)
	* 
	* 如果接收的是double类型数据,
	* T 模板参数就会推演成 double类型,(模板参数推演)
	* 然后就会示例化出具体的函数:
	* T 是double类型的对应函数。(模板实例化)
	*/

	return 0;
}


//如果一个函数需要接收不同类型的参数:
template<class T1, class T2>
/*
* 如果需要接收不同类型的参数,
* 直接在模板中设置多个模板参数即可,
*(模板参数名可以随便取,但一般会取为T -- type)
* 
* 模板参数 和 函数参数 类似,
* 但是 函数参数 定义的是 形参对象,
* 而 模板参数 定义的则是 类型
*/
void func(const T1& t1, const T2& t2)
//模板参数T1接收一种类型,T2接收另一种类型
{
	cout << t1 << endl;
	cout << t2 << endl;
	/*
	* 设置了模板参数的函数,
	* 如果要进行输入或输出,
	* 就必须使用 cin/cout 进行 输入/输出 了,
	* 因为设置了模板参数,
	* 不知道实际传进来的数据是什么数据,
	* 因为使用 scanf/printf 必须要指定数据类型,
	* 所以这里使用 scanf/printf 来 输入/输出
	*/
}


//通用(泛型)加法函数:
template<class T>
T Add(T left, T right)
//接收 T 模板参数类型
{
	return left + right;
	//返回值也是 T 模板类型
}


template<class T>
T* f()
{
	//开辟T类型的动态空间:
	T* p = new T[10];
	//没设置模板参数T,却使用了T

	//返回T类型指针:
	return p;

	/*
	* 该函数没有设置模板参数T,
	* (设置的参数不是模板参数)
	* 但返回值却返回模板指针类型(T*),
	* 
	* 没设置模板参数就无法进行类型推演
	*/
}


//主函数:
int main()
{
	/*
	*			推演实例化:
	* 函数参数传递,推演出模板参数的类型,
	* 再生成(实例化)对应的函数
	*/

	//隐式实例化:
	
	//T1推演为int,T2推演为int:
	func(1, 2);

	//T1推演为double,T2推演为double:
	func(1.1, 2.2);

	//T1推演为double,T2推演为int:
	func(1.1, 2);


	//调用通用(泛型)加法函数:
	cout << Add(1, 2.2) << endl;
	/*
	* 该语句不能通过编译,因为在编译期间,
	* 当编译器看到该实例化时,需要推演其实参类型
	* 通过实参a1将T推演为int,通过实参d1将T推演为double类型,
	* 但模板参数列表中只有一个T,
	* 编译器无法确定此处到底该将T确定为int 或者 double类型而报错
	* 
	* 注意:在模板中,编译器一般不会进行类型转换操作,
	* 因为一旦转化出问题,编译器就需要背黑锅
	* 
	* 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
	*/	
	cout << Add(1, (int)2.2) << endl;



	//显式实例化:
	
	// 2.2 隐式转换为int类型:
	cout << Add<int>(1, 2.2) << endl; 

	// 1 隐式转换为double类型:
	cout << Add<double>(1, 2.2) << endl;

	/*
	* 直接显式实例化,指定将参数实例化为某种类型,
	* 而不通过模板参数的类型推演
	*/

	//函数没有设置模板参数:
	double* p = f<double>();
	/*
	* 函数参数没设置模板参数,
	* 但却使用了模板参数,
	* 编译器没法进行类型推演,
	* 所以此时就需要显式实例化来确定类型
	* (显式实例化的真正用法)
	*/

	return 0;
}



//使用栈解决多类型问题:
typedef int STDataType;
/*
* 想让栈存储int类型数据,
* 就在这里设置类型为int,
* 想让栈存储double类型数据,
* 就在这里设置类型为double,
* ……
*/

类模板:
//template<class T>
//
类模板 -- 栈类:
//class Stack
//{
//public: //公有成员函数:
//
//	//构造函数:
//	Stack(int capacity = 4)
//	{
//		//调用了构造函数则打印:
//		cout << "Stack(int capacity = 4)" << endl;
//
//		//使用new开辟栈容量大小的空间:
//		
//		// typedef 设置多类型:
//		//_a = new STDataType[capacity];
//
//		// 类模板 设置多类型:
//		_a = new T[capacity]; //使用模板T类型
//
//		_top = 0; //栈顶值默认为0
//		_capacity = capacity; //设置栈容量
//	}
//
//	//析构函数:
//	~Stack()
//	{
//		//调用了析构函数则打印:
//		cout << "~Stack()" << endl;
//
//		//使用delete释放new开辟的空间:
//		delete[] _a;
//
//		_a = nullptr; //置为空指针
//		_top = 0; //栈顶值置为0
//		_capacity = 0; //栈容量置为0
//	}
//
//private: //私有成员变量:
//
//	T* _a; //栈指针 -- 使用模板T类型
//	int _top; //栈顶值
//	int _capacity; //栈容量
//
//};


//类模板:
template<class T>

//类模板 -- 栈类:
class Stack
{
public: //公有成员函数:

	//构造函数 -- 类模板成员函数声明和定义分离:
	Stack(int capacity = 4);


	//析构函数:
	~Stack()
	{
		//调用了析构函数则打印:
		cout << "~Stack()" << endl;

		//使用delete释放new开辟的空间:
		delete[] _a;

		_a = nullptr; //置为空指针
		_top = 0; //栈顶值置为0
		_capacity = 0; //栈容量置为0
	}

private: //私有成员变量:

	T* _a; //栈指针 -- 使用模板T类型
	int _top; //栈顶值
	int _capacity; //栈容量 

};

//类模板成员函数的声明和实现分离:
template<class T>
Stack<T>::Stack(int capacity)
/*
* 实现时指定的不是Stack成员函数的类名(类域),
* 而是Stack成员函数的类型,
* (不是Stack::,而是Stack<T>)
* 需要把模板参数写出来
* 
*				注:
* 类模板不允许声明和定义分离到两个文件,
* 分离时都写在一个.h文件中
*/
{
	//调用了构造函数则打印:
	cout << "Stack(int capacity = 4)" << endl;

	//使用new开辟栈容量大小的空间:

	// typedef 设置多类型:
	//_a = new STDataType[capacity];

	// 类模板 设置多类型:
	_a = new T[capacity]; //使用模板T类型

	_top = 0; //栈顶值默认为0
	_capacity = capacity; //设置栈容量
}


//主函数:
int main()
{
	/*
	* typedef 可以解决多类型问题,
	* 那 typedef 可以代替 模板 吗?
	* 
	* 答案是不能,typedef设置一个类的类型后,
	* 该类的类型就只能是typedef设置的那一种了,
	* 
	* 如果用类模板的话设置一个类的话,
	* 该类的一个对象就可以是int类型,
	* 而该类的另一个对象还可以是double类型
	*/

	//显式实例化:
	//让这个栈类型对象存储int类型:
	Stack<int> st1; //int

	//让这个栈类型对象存储double类型:
	Stack<double> st2; //double

	/*
	* 函数模板可以显式实例化,也可以让它自己推演,
	* 函数模板大多数情况让它自己推演出类型,
	* 不进行显式实例化
	* 
	* 类模板能让一个类的对象是不同类型的,
	* (如:都是栈类对象,但一个栈对象存储int类型数据,
	* 另一个栈对象存储double类型数据)
	* 只需要在创建对象时显式实例化需要的类型即可。
	* 这时我们实现的数据结构,
	* 就跟具体的存储类型是无关的,想要哪种类型,
	* 在创建对象时就显式实例化哪种类型
	*/

	return 0;
}



// 专门处理int的加法函数 -- 非模板函数
int Add(int left, int right)
{
	return left + right;
}


// 通用加法函数 -- 模板函数
template<class T>
T Add(T left, T right)
{
	return left + right;
}


//测试函数:
void Test()
{
	// 与非模板函数匹配,编译器不需要特化:
	Add(1, 2); 

	// 调用编译器特化的Add版本:
	Add<int>(1, 2); 
	//函数模板Add被实例化为非模板函数Add
}


//专门处理int的加法函数 -- 非模板函数
int Add(int left, int right)
{
	return left + right;
}

//通用加法函数 -- 模板函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
	return left + right;
}

//测试函数:
void Test()
{
	//Add(int, int):
	Add(1, 2);
	/*
	* 与非函数模板类型完全匹配,
	* 不需要函数模板实例化
	*/

	//Add(int, double):
	Add(1, 2.0);
	/*
	* 模板函数可以生成更加匹配的版本,
	* 编译器根据实参生成更加匹配的Add函数
	*/
}

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

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

相关文章

【C语言】数组(一维)详解,手把手教你,保姆级!!!

目录 数组的概念 数组的创建 数组的初始化 数组的类型 数组使用下标 数组的打印 数组的输入 数组的储存 总结 数组的概念 数组是⼀组相同类型元素的集合&#xff1b; 从这个概念中我们有3点拓展&#xff1a; 1&#xff0c;数组中存放的是1个或者多个数据&#xff0c;但…

【Linux】进程周边005之环境变量

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.环境变量是什么&#xff1…

引用阿里图标库,不知道对应的图标是什么,可在本地显示图标ui,再也不要担心刚来不知道公司图标对应的是什么了

项目中使用了阿里的图标库&#xff0c;但是无法看到对应显示什么&#xff0c;每次都要去阿里图标库里面找 在下载下来的文件中会发现有两个文件一个是iconfont.css和iconfont.json&#xff0c; 这两个文件的数据可以拿到然后显示在页面上 有两个问题&#xff1a; 1&#xff1a…

【Hadoop】执行start-dfs.sh启动hadoop集群时,datenode没有启动怎么办

执行start-dfs.sh后&#xff0c;datenode没有启动&#xff0c;很大一部分原因是因为在第一次格式化dfs后又重新执行了格式化命令&#xff08;hdfs namenode -format)&#xff0c;这时主节点namenode的clusterID会重新生成&#xff0c;而从节点datanode的clusterID 保持不变。 在…

ansible远程操作主机功能和自动化运维

ansible 两个功能&#xff1a;1、远程操作主机功能 2、自动化运维&#xff08;play 剧本 yaml&#xff09; 简述&#xff1a; 是基于python开发的配置管理和应用部署工具。在自动化运维中&#xff0c;现在是异军突起。 Asible能批量配置&#xff0c;部署&#xff0c;管理上千…

智能环境与可持续发展:人工智能为地球未来添翼

导言 在全球迅速发展的背景下&#xff0c;人工智能技术的应用逐渐深入到环境保护和可持续发展领域。随着全球环境问题的加剧&#xff0c;人工智能技术在环境和可持续发展领域的应用成为推动绿色未来的关键力量。本文将深入探讨人工智能在智能环境中的角色&#xff0c;以及如何通…

保护数据库数据安全就用行云管家!全方位保障!

对于企业而言&#xff0c;数据库是企业核心信息的存储和处理中心&#xff0c;所以保障数据库安全至关重要。而如何保障数据库数据的安全性也成为了企业亟待解决的问题。行云管家数据安全运维平台作为全面的数据安全运维平台&#xff0c;通过数据运维的事前阻断、事中控制、事后…

基于Java SpringBoot和Vue的医院信息管理挂号系统

摘要 医院信息管理系统&#xff08;Hospital Information Management System&#xff0c;简称HIMS&#xff09;是一种应用于医疗机构的信息化管理系统&#xff0c;旨在提高医疗服务质量、降低运营成本、提高工作效率和满足患者需求。HIMS通过对医院内各种信息的集成、管理和共享…

mybatis中oracle的sql没走索引导致特别慢(未加jdbcType的)

如果直接跑sql是能走索引很快&#xff0c;在mybatis中不能&#xff0c;可能就是jdbcType的原因。 比如&#xff0c;我有一个属性A&#xff0c;在表里面是VARCHAR2类型&#xff0c;但是在mybatis中的sql是#{a}&#xff0c;缺少jdbcTypeJdbcType.VARCHAR&#xff0c;就会导致myba…

Excel小技能:excel如何将数字20231211转化成指定日期格式2023/12/11

给了一串数字20231211&#xff0c;想要转成指定格式的日期格式&#xff0c;发现设置单元格格式为指定日期格式不生效&#xff0c;反而变成很长很长的一串#这个&#xff0c;如图所示&#xff1a; 其实&#xff0c;正确的做法如下&#xff1a; 1&#xff09;打开数据功能界面&am…

列举python2和python3的区别,python 2和python 3的区别

大家好&#xff0c;本文将围绕python2和python3的区别有哪些?展开说明&#xff0c;列举 python2和python3的区别?是一个很多人都想弄明白的事情&#xff0c;想搞清楚python2和python3的区别大吗需要先了解以下几个事情。 python不同于其他语言,python3并不对python2向下兼容 …

Java刷题篇——单链表练习题上

206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 1. 题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例1 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例2 输入&#xff1a;head [1,2] 输出&…

深入比较Input、Change和Blur事件:Vue与React中的行为差异解析

目录 前言 1. Input事件&#xff1a; 行为差异&#xff1a; 2. Change事件&#xff1a; 行为差异&#xff1a; 3. Blur事件&#xff1a; 行为差异&#xff1a; 4. 在Vue中的表现&#xff1a; Input事件&#xff1a; Change事件&#xff1a; Blur事件&#xff1a; 5.…

基于Vue的汽车服务商城系统设计与实现论文

摘 要 本课题是根据用户的需要以及网络的优势建立的一个基于Vue的汽车服务商城系统&#xff0c;来更好的为用户提供服务。 本基于Vue的汽车服务商城系统应用Java技术&#xff0c;MYSQL数据库存储数据&#xff0c;基于SSMVue框架开发。在网站的整个开发过程中&#xff0c;首先对…

【数据结构】树状数组总结

知识概览 树状数组有两个作用&#xff1a; 快速求前缀和 时间复杂度O(log(n))修改某一个数 时间复杂度O(log(n)) 例题展示 1. 单点修改&#xff0c;区间查询 题目链接 活动 - AcWing本活动组织刷《算法竞赛进阶指南》&#xff0c;系统学习各种编程算法。主要面向…

浅谈深度学习中的不同归一化层

引言 目前&#xff0c;深度学习已经彻底改变了自然语言处理、计算机视觉、机器人等许多子领域。深度学习当然涉及训练精心设计的深度神经网络&#xff0c;并且各种设计决策会影响这些深度网络的训练机制。其中一些设计决策包括 网络中要使用的网络层类型&#xff0c;例如卷积…

【python】深拷贝和浅拷贝

能使用.copy()的对象&#xff1a; 需要是能改变元素的对象比如 list 和 set 就可以改变对象&#xff0c;可以使用copy函数但是类似于 一个整数 a10 或者 元组 就不能使用copy函数&#xff0c;因为他们是不可改变的对象 深拷贝和浅拷贝 浅拷贝就是这能复制第一层元素&#xff0…

12V转24V10A升压同步整流芯片:高效能解决方案

12V转24V10A升压同步整流芯片&#xff1a;高效能解决方案 随着现代电子设备的日益普及&#xff0c;对电源管理的要求也越来越高。其中&#xff0c;升压同步整流芯片在提高电源转换效率方面发挥着重要作用。本文将为您介绍一款12V转24V10A升压同步整流芯片&#xff0c;其优异的…

Kafka 基础快速入门

1、生产者 1、生产者发送消息流程 配置生产者参数属性和创建生产者对象 构建消息:ProducerRecord 发送消息:Send 关闭生产者 2、消费者 1、消费者接受消息流程 配置消费者参数属性和创建消费者对象 订阅主题 拉取消息并进行消费处理 提交消费偏移量,关闭消费者 2、消费者和…

前后端传参中遇见的问题

前后端传参经常容易出错&#xff0c;本文记录开发springBootMybatis-plusvuecli项目中出现的传参问题及解决办法 1.前后端没有跨域配置&#xff0c;报错 解决方法&#xff1a;后端进行跨域配置&#xff0c;拷贝CorsConfig类 package com.example.xxxx.config;import org.spr…