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

C++初阶——模板

C++初阶——模板

一、概念引入

1.如何实现一个通用的交换函数,使它既可以用来交换各种类型的数据呢?

通过前面的学习,我们知道函数重载可以帮我们实现这一功能,代码如下:
示例1
运行结果如图:
示例2
使用函数重载虽然可以实现,但是有一下几个不好的地方:

  • 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
  • 代码的可维护性比较低,一个出错可能所有的重载均出错

那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?

  • C++引入了模板的概念,就像在模具中浇筑一样,我们只需要浇筑不同的材料液,就能到由对应材料的物体。C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(数据类型),来获得不同材料的铸件(即生成具体类型的代码

2.模板的分类

示例3
模板分为函数模板类模板,模板是泛型编程的基础。什么是泛型编程,泛型编程就是编写与类型无关的通用代码,是代码复用的一种手段。

二、函数模板

1.函数模板的概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被实例化,根据实参类型产生函数的特定类型版本

2.函数模板的格式

template<typename T1, typename T2,...,typename Tn>
返回值类型 函数名(参数列表) { //语句 }
我们来简单的使用一下,就以交换函数为例,如图所示:
示例4
template是C++泛型编程的核心关键字,用于定义与数据类型无关的代码模板,这里的语法是固定的格式,template<typename T1, typename T2,......,typename Tn> typename的意思就是类型名,表示这里的T既可以是int T,也可以是double T,还可以是char T等等。这里的传参传递的是引用,引用相较于指针更具优势,因此在前面我们也讲过能用引用传参的地方就尽量用引用传参
总体看来,函数模板还是很好理解的,只是把之前的具体数据类型替换成了模板而已。
测试代码如下:
示例5
运行结果如图:
示例6

3.函数模板的原理

刚才我们写了一个函数模板,void Swap(T& x1, T& x2),函数模板是一个蓝图,它本身并不是函数,在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此,编译器处理函数模板是要生成对应的具体函数的,如图所示:
示例7

4.函数模板的实例化

用不同类型的参数使用函数模板时,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。

(1)隐式实例化:让编译器根据实参推演模板参数的实际类型

我们来看这样一段代码:
示例8
我们来分析一下,这里的第一个函数还是刚才的交换函数,我们已经很熟悉了,下面的一个Func函数有一点不一样,这里安排了两个模板参数T1T2。这里编译器会自动根据传递的参数类型来分别判断应该示例化成什么数据类型,比如说这里的Swap传递的两个值必须是同一个数据类型,如果不同,就会出现矛盾,因为这里只有一个模板参数,如果前一个是int型,实例化出的函数的功能是交换两个整数,如果后一个传递的是float,编译器又会将它实例化成交换两个浮点型的函数,前后会出现矛盾,而且这里也不存在什么隐式类型转换,整型提升等等;而Func函数接收的两个值可以是不同的数据类型,因为这里有两个不同的模板参数,不会出现矛盾。我们来看一下这里的运行结果:
示例9
如果Swap函数传递的不是同一个数据类型,就会出现报错:
示例10
示例11

(2)显式实例化:在函数名后的<>中指定模板参数的实际类型

我们来看这样一段代码:
示例12
这里的加法函数也只有一个模板参数,也就是说,只能实现同种数据类型的加法,如果是两个不同类型的数据相加,则需要在函数名Add后的<>中指定模板参数的实际类型,这就是显示实例化,就好比告诉编译器:到底听谁的。
运行结果如图:
示例13

5.模板参数的匹配原则

(1)一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

示例14

(2)对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

示例15

(3)模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

示例16
这里的普通函数虽然接受的参数是两个int型,但如果传递的数据类型有所不同,它也会进行自动类型转换;而模板函数就会发生报错,在上文中也做出了详细解释。

三、类模板

1.类模板的格式

template<class T1, class T2, ..., class Tn>
class 类模板名
{//类内成员定义
};

2.类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

我们以动态顺序表为例:

typedef int DataType;
class StackInt
{
public:StackInt(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~StackInt(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:DataType* _array;int _capacity;int _size;
};typedef double DataType;
class StackDouble
{
public:StackDouble(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~StackDouble(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:DataType* _array;int _capacity;int _size;
};int main()
{StackInt s1;//intStackDouble s2;//doublereturn 0;
}

这里写了两种顺序表,如果能合二为一,根据需要自动推演就更好了,所以我们使用类模板:

template<class T>
class Stack
{
public:Stack(size_t capacity = 3);void Push(const T& data);~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:T* _array;int capacity;int _size;
};template<class T>
Stack<T>::Stack(size_t capacity)
{_array = new T[capacity];_capacity = capacity;_size = 0;
}template<class T>
void Stack<T>::Push(const T& data)
{//CheckCapacity();_array[_size] = data;_size++;
}

这里采用了类里声明,类外定义的方式,要注意的是,在类外声明时,还要加上template关键字声明模板参数,因为它的作用域只到紧靠着它的函数。

3.typename和class

在上面的代码中,我们注意到<>中的typename变成了class,在C++模板编程中,typenameclass均用于声明模板类型参数,但二者在语义、使用场景和代码意图上存在细微差异。至于具体是什么,我们以后会介绍。二者在绝大多数情况下没什么区别,建议优先使用typename

四、代码资源

本期内容涉及到的代码如下:

#include<iostream>using namespace std;//void Swap(int& left, int& right)
//{
//	int tmp = left;
//	left = right;
//	right = tmp;
//}
//void Swap(char& left, char& right)
//{
//	char tmp = left;
//	left = right;
//	right = tmp;
//}
//void Swap(double& left, double& right)
//{
//	double tmp = left;
//	left = right;
//	right = tmp;
//}
//int main()
//{
//	int a = 1;
//	int b = 2;
//	printf("交换前:a=%d,b=%d\n", a, b);
//	Swap(a, b);
//	printf("交换后:a=%d,b=%d\n", a, b);
//
//	char c = 'a';
//	char d = 'b';
//	printf("交换前:%c,%c\n", c, d);
//	Swap(c, d);
//	printf("交换后:%c,%c\n", c, d);
//
//	double e = 1.56;
//	double f = 1.89;
//	printf("交换前:e=%lf,f=%lf\n", e, f);
//	Swap(e, f);
//	printf("交换后:e=%lf,f=%lf\n", e, f);
//
//	return 0;
//}//template<typename T>
//void Swap(T& x1, T& x2)
//{
//	T tmp = x1;
//	x1 = x2;
//	x2 = tmp;
//}
//
//int main()
//{
//	int a = 1;
//	int b = 2;
//	cout << a << ',' << b << endl;
//	Swap(a, b);
//	cout << a << ',' << b << endl;
//
//	double c = 1.56;
//	double d = 1.89;
//	cout << c << ',' << d << endl;
//	Swap(c, d);
//	cout << c << ',' << d << endl;
//
//	char e = 'a';
//	char f = 'b';
//	cout << e << ',' << f << endl;
//	Swap(e, f);
//	cout << e << ',' << f << endl;
//
//	return 0;
//}// 泛型编程
// 函数模板// 函数模板实例化生成具体函数
// 函数模板根据调用,自己推导模板参数的类型,实例化出对应的函数//template<typename T>
//void Swap(T& x1, T& x2)
//{
//	T tmp = x1;
//	x1 = x2;
//	x2 = tmp;
//}
//
//template<typename T1,typename T2>
//T1 Func(const T1& x, const T2& y)
//{
//	cout << x << " " << y << endl;
//	return x;
//}
//
//int main()
//{
//	int a = 1;
//	int b = 2;
//	cout << a << ',' << b << endl;
//	Swap(a, b);
//	cout << a << ',' << b << endl;
//	
//	double c = 1.56;
//	double d = 1.89;
//	cout << c << ',' << d << endl;
//	Swap(c, d);
//	cout << c << ',' << d << endl;
//
//	Func(5, 6);
//	Func(1, 1.23);
//
//	return 0;
//}//template<typename T>
//T Add(const T& left, const T& right)
//{
//	return left + right;
//}
//
//int main()
//{
//	int a = 1;
//	int b = 2;
//	cout << Add(a,b) << endl;
//
//	double c = 1.5;
//	double d = 1.6;
//	cout << Add(c, d) << endl;
//
//	//cout << Add(a, d) << endl;
//
//	cout << Add<int>(a, d) << endl;
//	cout << Add<double>(a, d) << endl;
//
//	return 0;
//}//有些函数无法自动推,只能显示实例化
//template<typename T>
//T* Alloc(int n)
//{
//	return new T[n];
//}
//
//int main()
//{
//	double* p1 = Alloc<double>(10);
//
//	return 0;
//}//typedef int DataType;
//class StackInt
//{
//public:
//	StackInt(size_t capacity = 3)
//	{
//		_array = (DataType*)malloc(sizeof(DataType) * capacity);
//		if (NULL == _array)
//		{
//			perror("malloc申请空间失败!!!");
//			return;
//		}
//		_capacity = capacity;
//		_size = 0;
//	}
//	void Push(DataType data)
//	{
//		// CheckCapacity();
//		_array[_size] = data;
//		_size++;
//	}
//	// 其他方法...
//	~StackInt()
//	{
//		if (_array)
//		{
//			free(_array);
//			_array = NULL;
//			_capacity = 0;
//			_size = 0;
//		}
//	}
//private:
//	DataType* _array;
//	int _capacity;
//	int _size;
//};
//
//typedef double DataType;
//class StackDouble
//{
//public:
//	StackDouble(size_t capacity = 3)
//	{
//		_array = (DataType*)malloc(sizeof(DataType) * capacity);
//		if (NULL == _array)
//		{
//			perror("malloc申请空间失败!!!");
//			return;
//		}
//		_capacity = capacity;
//		_size = 0;
//	}
//	void Push(DataType data)
//	{
//		// CheckCapacity();
//		_array[_size] = data;
//		_size++;
//	}
//	// 其他方法...
//	~StackDouble()
//	{
//		if (_array)
//		{
//			free(_array);
//			_array = NULL;
//			_capacity = 0;
//			_size = 0;
//		}
//	}
//private:
//	DataType* _array;
//	int _capacity;
//	int _size;
//};
//
//int main()
//{
//	StackInt s1;//int
//	StackDouble s2;//double
//
//	return 0;
//}//template<class T>
//class Stack
//{
//public:
//	Stack(size_t capacity = 3);
//	void Push(const T& data);
//	~Stack()
//	{
//		if (_array)
//		{
//			free(_array);
//			_array = NULL;
//			_capacity = 0;
//			_size = 0;
//		}
//	}
//private:
//	T* _array;
//	int capacity;
//	int _size;
//};
//
//template<class T>
//Stack<T>::Stack(size_t capacity)
//{
//	_array = new T[capacity];
//	_capacity = capacity;
//	_size = 0;
//}
//
//template<class T>
//void Stack<T>::Push(const T& data)
//{
//	//CheckCapacity();
//	_array[_size] = data;
//	_size++;
//}// 专门处理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<int>(1, 2); //调用编译器特化的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(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
//	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
//}//int Add(int left, int right)
//{
//	return left + right;
//}
//int main()
//{
//	int a = 1;
//	double b = 1.89;
//	cout << Add(a, b) << endl;
//	return 0;
//}

五、本期总结+下期预告

本期内容我们讲解了C++中的模板,接下来就会正式进入C++标准模板库STL的讲解!

感谢大家的关注,我们下期再见!
在这里插入图片描述

相关文章:

C++初阶——模板

C初阶——模板 一、概念引入 1.如何实现一个通用的交换函数&#xff0c;使它既可以用来交换各种类型的数据呢&#xff1f; 通过前面的学习&#xff0c;我们知道函数重载可以帮我们实现这一功能&#xff0c;代码如下&#xff1a; 运行结果如图&#xff1a; 使用函数重载虽然…...

【技术派后端篇】技术派中基于 Redis 的缓存实践

在互联网应用追求高并发和高可用的背景下&#xff0c;缓存对于提升程序性能至关重要。相较于本地缓存 Guava Cache 和 Caffeine&#xff0c;Redis 具有显著优势。Redis 支持集群和分布式部署&#xff0c;能横向扩展缓存容量和负载能力&#xff0c;适应大型分布式系统的缓存需求…...

系统安装及应用

重点 账号安全控制 系统引导和登陆控制 弱口令检测 端口扫描 前言 随着信息技术的快速发展,系统安全成为我们日常生活和工作中不可或缺的一部分。本章节主要探讨系统安全及应用,涵盖了账号安全控制、系统引导和登录控制、弱口令检测以及端口扫描等多个方面,为我们提供了一…...

发布事件和Insert数据库先后顺序

代码解释 csharp await PublishCreatedAsync(entity).ConfigureAwait(false); await Repository.InsertAsync(entity).ConfigureAwait(false);PublishCreatedAsync(entity)&#xff1a;这是一个异步方法&#xff0c;其功能是发布与实体创建相关的事件。此方法或许会通知其他组…...

【英语语法】词法---冠词

目录 冠词一、不定冠词&#xff1a;a / an1. 基本用法2. 主要使用场景3. 特殊情况 二、定冠词&#xff1a;the1. 基本用法2. 主要使用场景3. 特殊情况 三、零冠词1. 基本规则2. 特殊情况 四、冠词对比五、常见错误总结 冠词 冠词是英语中用于限定名词的一类虚词&#xff0c;分…...

android的 framework 有哪些知识点和应用场景

Android Framework 知识点 1. 四大组件 Activity&#xff08;活动&#xff09; 是 Android 应用中最基本的组件&#xff0c;用于实现用户界面。一个 Activity 通常对应一个屏幕的内容。有自己的生命周期&#xff0c;包括 onCreate、onStart、onResume、onPause、onStop、onDe…...

Prompt 攻击与防范:大语言模型安全的新挑战

随着大语言模型&#xff08;LLM&#xff09;在企业服务、智能助手、搜索增强等领域的广泛应用&#xff0c;围绕其"Prompt"机制的安全问题也逐渐引起关注。其中最具代表性的&#xff0c;就是所谓的 Prompt Injection&#xff08;提示词注入&#xff09;攻击。 本文将…...

Ubuntu20.04安装Pangolin遇到的几种报错的解决方案

1.添加两个编译选项 /usr/include/OpenEXR/half.h:121:13: note: because ‘half’ has user-provided ‘half& half::operator(half)’121 | half & operator (half h);| ^~~~~~~~ 解决方案&#xff1a; 在CMakeList中添加以下两句&#xff1a; …...

软考 中级软件设计师 考点知识点笔记总结 day14 关系代数 数据库完整性约束

文章目录 6.5 关系代数6.5.1 关系代数—七种基本运算 6.6 数据库完整性约束6.7 关系型数据库SQL简介 6.5 关系代数 候选码&#xff08;键&#xff09;&#xff1a;若关系中的某一属性或属性组的值能唯一标识一个元组&#xff0c;则称该属性或属性组为候选码。 主码&#xff0…...

前端vue监听 -watch

前端vue监听 -watch 前言基本用法监听简单数据属性监听对象属性 高级用法深度监听对象即时触发监听监听计算属性 注意事项 前言 在 Vue.js 里&#xff0c;watch 选项可用于响应式地监听数据的变化&#xff0c;当被监听的数据发生改变时&#xff0c;就会触发相应的回调函数来执…...

Linux之信号

目录 一、预备知识 二、信号的产生 一、键盘产生信号 二、系统调用 三、调用系统命令向进程发信号 kill 四、硬件异常 五、软件条件 三、信号的保存 四、信号的处理 一、预备知识 1.信号&#xff01;信号量。两者没有任何关系 2.什么是信号&#xff1f; 定义一&…...

微软Edge浏览器字体设置

前言 时间&#xff1a;2025年4月 自2025年4月起&#xff0c;微软Edge浏览器的默认字体被微软从微软雅黑替换成了Noto Sans&#xff0c;如下图。Noto Sans字体与微软雅黑风格差不多&#xff0c;但在4K以下分辨率的显示器上较微软雅黑更模糊&#xff0c;因此低分辨率的显示器建议…...

Java中 关于编译(Compilation)、类加载(Class Loading) 和 运行(Execution)的详细区别解析

以下是Java中 编译&#xff08;Compilation&#xff09;、类加载&#xff08;Class Loading&#xff09; 和 运行&#xff08;Execution&#xff09; 的详细区别解析&#xff1a; 1. 编译&#xff08;Compilation&#xff09; 定义 将Java源代码&#xff08;.java文件&#x…...

[python] set

1.添加元素 在 Python 中&#xff0c;向 set 添加一个元素可以使用 add() 方法。如果添加的元素已经存在于 set 中&#xff0c;add() 不会重复添加&#xff08;因为 set 具有自动去重的特性&#xff09;。 方法 1&#xff1a;add(element)&#xff08;添加单个元素&#xff0…...

转化率提升47%?亚马逊用户行为预测模型深度解读

在亚马逊运营的战场上&#xff0c;谁能更精准地读懂用户行为&#xff0c;谁就更可能赢得转化率的胜利。近年来&#xff0c;越来越多卖家借助“用户行为预测模型”来优化Listing布局、广告投放策略、甚至库存管理&#xff0c;而这些数据驱动的决策也确确实实地带来了质的提升。 …...

C++计算 n! 中末尾零的数量

* 详细说明* 给定一个整数作为输入。目标是找出该数的阶乘结果中末尾零的数量。 一个数 N 的阶乘是范围 [1, N] 内所有数的乘积。* * 我们知道&#xff0c;只有当一个数是 10 的倍数或者有因数对 (2, 5) 时&#xff0c;才会产生末尾零。 在任何大于 5 的数的阶乘中&#xff0c;…...

大模型中超参数TopK是什么

大模型中的超参数Top-K是文本生成过程中的关键控制参数,主要用于平衡生成结果的确定性与多样性。以下从定义、工作原理、应用场景及与其他参数的协同关系进行详细阐述: 一、Top-K的定义与核心机制 基本定义 Top-K(Top-K Sampling)是一种基于概率采样的文本生成策略。其核心…...

NetApp ONTAP 9 故障磁盘更换操作指南

以前写过一篇7-mode的磁盘更换文档&#xff0c;好几个朋友反馈说命令都没有&#xff0c;都不对。主要原因是客户现在的环境都是ontap 9的cluster-mode环境了&#xff0c;所以很多命令都不一样了。为此&#xff0c;这里专门就ontap 9的cluster-mode写一篇磁盘更换操作指南&#…...

leetcode day 35 01背包问题 416+1049

0-1背包问题 &#xff08;1&#xff09;第一种情况&#xff1a;二维dp[i][j]数组 dp[i][j]表示[0,i]的物品放入容量为j背包的最大价值 不放物品i,dp[i][j]dp[i-1][j] 放物品i,dp[i][j]dp[i-1][j-w[i]]v[i] 递推公式为&#xff1a; dp[i][j]dp[i-1][j];//不放 if(w[i]<j)dp…...

MySQL的基本操作

显示所有数据库&#xff1a; SHOW DATABASES; 系统默认数据库&#xff1a; 数据库名用途information_schema存储 MySQL 服务器元数据&#xff08;如数据库、表、列信息&#xff09;&#xff0c;只读mysql存储用户权限、密码、日志等核心数据&#xff08;不要随意修改&#xff…...

CSS伪类、clip-path实现三角形、箭头绘制

<template><div :class"$options.name"><div class"triangle-container1"><!-- 伪类三角形&#xff1a;向右 --><div class"triangle-RM"></div><!-- 伪类三角形&#xff1a;向下 --><div class&q…...

基于大模型的腹股沟疝全流程预测与诊疗方案研究报告

目录 一、引言 1.1 研究背景与目的 1.2 研究方法与创新点 二、大模型在腹股沟疝术前评估中的应用 2.1 腹股沟疝概述与诊断方法 2.2 术前评估指标与数据收集 2.3 大模型预测原理与实现 2.4 预测结果与传统评估对比 三、基于大模型预测的手术方案制定 3.1 手术方式选择…...

零基础上手Python数据分析 (20):Seaborn 统计数据可视化 - 轻松绘制精美统计图表!

写在前面 —— 告别 Matplotlib 繁琐定制,拥抱 Seaborn 便捷之美,让统计可视化更高效 在前面两篇博客中,我们学习了 Python 数据可视化的基石 Matplotlib,掌握了绘制基础图表和进行高级定制的技巧。 Matplotlib 功能强大且灵活,能够满足几乎所有的二维绘图需求。 然而,…...

elasticsearch7.15节点磁盘空间满了迁移数据到新磁盘

一.数据安全迁移 在 Elasticsearch 中设置某个节点临时不可用&#xff08;例如进行维护或升级&#xff09;&#xff0c;可以通过以下步骤安全地操作&#xff0c;避免数据丢失或集群状态异常 1: 排除节点分片分配&#xff0c;触发分片迁移到其他节点 PUT /_cluster/settings {&…...

MCP案例—客户端和服务端

MCP简介 Model Context Protocol (模型上下文协议)&#xff0c;简称MCP&#xff0c;MCP是一种协议&#xff0c;用于LLM与外部拓展资源交互的协议。 想了解具体细节可参考作者本篇文章MCP理论指南 准备 本篇文章将带你通过python创建MCP客户端及服务端&#xff0c;并连接到本…...

排序模型(Learning to Rank)

排序模型&#xff08;Learning to Rank&#xff09; 要解决的问题 排序模型旨在解决信息检索中的排序优化问题。例如&#xff1a; 搜索引擎中对候选网页的排序推荐系统中物品的展示顺序广告系统中广告位的分配 核心挑战&#xff1a;根据上下文特征&#xff0c;将最相关/最有…...

L1-1、Prompt 是什么?为什么它能“控制 AI”?

*Prompt 入门 L1-1 想象一下&#xff0c;你只需输入一句话&#xff0c;AI 就能自动为你写一篇文案、生成一份报告、甚至规划你的创业计划。这种“对话即编程”的背后魔法&#xff0c;就是 Prompt 的力量。 &#x1f50d; 一、Prompt 的定义与由来 Prompt&#xff08;提示词&am…...

RolmOCR重磅开源:基于Qwen2.5-VL,速度提升40%,手写/倾斜文档识别准确率超92%

向大家介绍一款全新的开源OCR模型——RolmOCR&#xff01;这款由Reducto AI团队基于阿里巴巴强大的Qwen2.5-VL-7B-Instruct视觉语言模型微调而来的利器&#xff0c;不仅在速度和效率上实现了显著提升&#xff08;据称处理速度相比其前身olmOCR提升了约40%&#xff09;&#xff…...

系统架构设计(二):基于架构的软件设计方法ABSD

“基于架构的软件设计方法”&#xff08;Architecture-Based Software Design, ABSD&#xff09;是一种通过从软件架构层面出发指导详细设计的系统化方法。它旨在桥接架构设计与详细设计之间的鸿沟&#xff0c;确保系统的高层结构能够有效指导后续开发。 ABSD 的核心思想 ABS…...

[langchain教程]langchain03——用langchain构建RAG应用

RAG RAG过程 离线过程&#xff1a; 加载文档将文档按一定条件切割成片段将切割的文本片段转为向量&#xff0c;存入检索引擎&#xff08;向量库&#xff09; 在线过程&#xff1a; 用户输入Query&#xff0c;将Query转为向量从向量库检索&#xff0c;获得相似度TopN信息将…...

Android 图片加载框架 Glide 详细介绍

一、简单使用 1、加载图片 导入依赖 implementation("com.github.bumptech.glide:glide:4.16.0")编写代码 private static final String url = "http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg";btnPic.setOnClickList…...

vue2解析html中的公式,使用vue-katex

文本是markdown格式&#xff0c;需要解析markdown <p v-html"md.render(text)"></p>import MarkdownIt from markdown-it ...const mdRender MarkdownIt(); ...data中md: new MarkdownIt(),现在文本中会出现数学公式&#xff0c;解析使用vue-katex 1.…...

使用Unity Cache Server提高效率

2021年1月20日19:04:28 1 简介 Unity Cache Server,翻译过来就是Unity缓存服务器 1.1 缓存服务器の官方介绍 Unity 有一个完全自动的资源管线。每当修改 .psd 或 .fbx 文件等源资源时,Unity 都会检测到更改并自动将其重新导入。随后,Unity 以内部格式存储从文件导入的数…...

【C++】模板2.0

最近学习了一些模板的知识&#xff0c;速写本博客作为学习笔记&#xff0c;若有兴趣&#xff0c;欢迎垂阅读&#xff01; 1.非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名…...

深入解析 Linux 文件系统中的软硬链接:从原理到实践

引言 在 Linux 系统中&#xff0c;软链接&#xff08;Symbolic Link&#xff09; 和 硬链接&#xff08;Hard Link&#xff09; 是文件管理的两大核心机制。它们如同文件系统的“快捷方式”与“分身术”&#xff0c;既能节省存储空间&#xff0c;又能实现灵活的文件管理。但两…...

JumpServer多用户VNC桌面配置指南:实现多端口远程访问

在当今的云计算和远程工作环境中,高效且安全地管理多用户远程桌面访问变得越来越重要。本文将详细介绍如何在JumpServer中配置多个VNC桌面,以满足不同用户的远程访问需求。我们将以创建第二个桌面为例,为用户user2配置VNC访问。 一、背景说明 JumpServer作为一款优秀的开源…...

【数据结构入门训练DAY-19】总结数据结构中的栈

文章目录 前言一、栈的思想二、栈的解题思路结语 前言 本次训练内容&#xff1a; 栈的复习。总结栈的基本操作 一、栈的思想 在数据结构中&#xff0c;栈是一种很常见的算法。栈——就像你往桶里放东西似的&#xff0c;要取出桶内的物体就得先把桶顶的物品取出来&#xff…...

MyBatis-Plus 防止 SQL 注入最佳实践指南

&#x1f6ab; MyBatis-Plus 防止 SQL 注入最佳实践指南 作者&#xff1a;William Dawson 标签&#xff1a;Java、MyBatis-Plus、安全、SQL 注入、防护 &#x1f4a5; 什么是 SQL 注入&#xff1f; SQL 注入是一种常见的安全漏洞&#xff0c;攻击者通过恶意构造 SQL 输入参数&…...

AI之pdf解析:Tesseract、PaddleOCR、RapidPaddle(可能为 RapidOCR)和 plumberpdf 的对比分析及使用建议

目录标题 Tesseract、PaddleOCR、RapidPaddle&#xff08;可能为 RapidOCR&#xff09;和 plumberpdf 的对比分析1. Tesseract类型: 开源 OCR 引擎特点:缺点:适用场景: 2. PaddleOCR (推荐)类型:特点:缺点:适用场景: 复杂版式文档、多语言混合文本、需要高精度识别的场景&#…...

经典文献阅读之--Kinematic-ICP(动态优化激光雷达与轮式里程计融合)

0. 简介 传统的激光雷达里程计系统通过点云配准来计算移动机器人的自运动&#xff08;ego-motion&#xff09;&#xff0c;但它们通常没有考虑机器人的运动学特性&#xff0c;这可能导致不准确的运动估计&#xff0c;特别是在机器人不可能发生某些运动&#xff08;如沿z轴的小…...

【显卡占用】kill程序后,显卡仍被占用

如果 kill 程序执行了&#xff0c;但显卡仍然显示被占用&#xff0c;咋个办&#xff1f; 如图所示&#xff0c;GPU-Util占用为0%&#xff0c;但显示占用48G&#xff0c;且无法再上程序&#xff1a; 执行命令&#xff1a; fuser -v /dev/nvidia* kill pid若上述方法无法解决&am…...

在 macOS 上合并 IntelliJ IDEA 的项目窗口

在使用 IntelliJ IDEA 开发时&#xff0c;可能会打开多个项目窗口&#xff0c;这可能会导致界面变得混乱。为了提高工作效率&#xff0c;可以通过合并项目窗口来简化界面。本文将介绍如何在 macOS 上合并 IntelliJ IDEA 的项目窗口。 操作步骤 打开 IntelliJ IDEA: 启动你的 I…...

IO流--字节流详解

IO流 用于读写数据的&#xff08;可以读写文件&#xff0c;或网络中的数据&#xff09; 概述&#xff1a; I指 Input&#xff0c;称为输入流&#xff1a;负责从磁盘或网络上将数据读到内存中去 O指Output&#xff0c;称为输出流&#xff0c;负责写数据出去到网络或磁盘上 因…...

6N60-ASEMI机器人功率器件专用6N60

编辑&#xff1a;ll 6N60-ASEMI机器人功率器件专用6N60 型号&#xff1a;6N60 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220F 批号&#xff1a;最新 最大漏源电流&#xff1a;6A 漏源击穿电压&#xff1a;600V RDS&#xff08;ON&#xff09;Max&#xff1a;1.20Ω …...

实现侧边栏点击标题列表,和中间列表区域联动效果

左侧边栏标题列表实现&#xff1a; -------------------html-----------------------<divclass"uav":class"{ hidden: !isVisible, visible: isVisible }"><ul id"toc"><liv-for"(item, index) in HotList":key"…...

基于MuJoCo物理引擎的机器人学习仿真框架robosuite

Robosuite 基于 MuJoCo 物理引擎&#xff0c;能支持多种机器人模型&#xff0c;提供丰富多样的任务场景&#xff0c;像基础的抓取、推物&#xff0c;精细的开门、拧瓶盖等操作。它可灵活配置多种传感器&#xff0c;提供本体、视觉、力 / 触觉等感知数据。因其对强化学习友好&am…...

kafka监控kafka manager(CMAK)部署配置

一、准备工作 1.1、服务器信息梳理 角色IP操作系统安装服务监控机10.45.19.20Linux CentOS 7.9CMAK3.0.0.5、ZooKeeper3.9.0、JDK11、JDK1.8被监控机 Kafka broker.id 050.50.50.101Linux CentOS 7.9Kafka、ZooKeeper&#xff08;任意版本&#xff09;被监控机 Kafka broker.…...

线程池的介绍

目录 一、什么是线程池 二、线程池的详细内容 三、线程池的简化 一、什么是线程池 提到线程池&#xff0c;我们可能想到 常量池&#xff0c;可以先来说说常量池&#xff1a; 像是字符串常量&#xff0c;在Java程序最初构建的时候&#xff0c;就已经准备好了&#xff0c;等程…...

day33和day34图像处理OpenCV

文章目录 一、图像预处理12 图像梯度处理12.3 Sobel算子12.4 Laplacian算子1.原理&#xff1a;2.语法&#xff1a; 13 图像边缘检测思路13.1 高斯滤波去噪点13.2 计算图像的梯度与方向13.3 非极大值抑制13.4 双阈值筛选13.5 Canny方法和使用 14 绘制图像轮廓14.1 什么是轮廓14.…...

电脑硬盘常见的几种接口类型

一、传统接口&#xff08;机械硬盘为主&#xff09; 1. SATA 接口&#xff08;Serial ATA&#xff09; 特点&#xff1a; 最主流的机械硬盘&#xff08;HDD&#xff09;接口&#xff0c;广泛用于台式机和笔记本电脑。传输速度较慢&#xff0c;理论最大带宽为 6 Gbps&#xff…...