C++泛型编程——模板

C++泛型编程——模板

文章目录

  • C++泛型编程——模板
  • 1. 泛型编程的概念
  • 2. 模板
    • 2.1 模板格式
    • 2.2 函数模板
      • 2.3 函数模板的实例化
        • 2.3.1 隐式(推演)实例化
        • 2.3.2 显式实例化
    • 2.3 类模板
    • 2.4 非类型模板参数
    • 2.5 模板的特化
      • 2.5.1 函数模板的特化
      • 2.5.2 类模板的特化
        • 2.5.2.1 全特化
        • 2.5.2.2 偏特化
    • 3. 模板的本质

本章思维导图:
在这里插入图片描述

注:本章思维导图对应的xmind.png文件都已同步导入至资源


1. 泛型编程的概念

在C++中,如果我们不借助库函数,要实现两个数据的交换函数swap,由于要考虑到数据类型的多样性,我们难免要将swap函数重载很多次,例如:

void swap(int& num1, int& num2)
{
	int temp = num1;
	num1 = num2;
	num2 = temp;
}

void swap(char& num1, char& num2)
{
	int temp = num1;
	num1 = num2;
	num2 = temp;
}

void swap(double& num1, double& num2)
{
	int temp = num1;
	num1 = num2;
	num2 = temp;
}

//…………………………

swap函数的函数体基本相同,只有交换数据的类型不同,但就是由于这个小小的不同迫使我们产生很多冗余代码,使生产效率变得低下

为了解决这一问题,C++就支持了泛型编程这一概念:

允许我们用一个通用的代码模板来处理不同类型数据

在C++中,泛型编程就是靠模板来实现的

2. 模板

2.1 模板格式

基本格式为:

template<typename T1, typename T2,......,typename Tn>

  • template为模板的关键字
  • <>里面的即模板参数,代表一个数据类型
  • typename可以用class替代

2.2 函数模板

在这里插入图片描述

基本格式为:

template<typename T1, typename T2,......,typename Tn>

返回值类型 函数名(参数列表){}

例如:

//定义一个函数模板swap
//T泛指所有类型

template <typename T>
void swap(T& num1, T& num2)
{
	T temp = num1;
	num1 = num2;
	num2 = temp;
}

注意:

当模板函数的声明和定义不能分布在不同的文件

2.3 函数模板的实例化

用不同的类型使用函数模版生成一个具体的函数这一过程叫做函数模板的实例化,函数模板的实例化有以下两种方法:

2.3.1 隐式(推演)实例化

推演实例化——让编译器根据实参推演模板参数的实际类型

例如:

template <typename T>
void swap(T& num1, T& num2)
{
	T temp = num1;
	num1 = num2;
	num2 = temp;
}


int main()
{
	int a = 1, b = 2;
	double a1 = 1.0, b1 = 2.0;

	swap(a, b);	//实例化函数模板为swap(int& num1, int& num2),并进行调用
	swap(a1, b1);	//实例化函数模板为swap(double& num1, double& num2),并进行调用

	return 0;
}

需要注意:
如果该函数模板的模板参数只有一个,那么进行推演实例化时,传入的实参的类型就只能有一个(即模板不允许自动类型转换

例如:

template <typename T>
void swap(T& num1, T& num2)
{
	T temp = num1;
	num1 = num2;
	num2 = temp;
}

int main()
{
	int a = 1;
	double b = 2.0;
	swap(a, b);

	return 0;
}

//会报错:
// “swap”: 未找到匹配的重载函数
// “void swap(T &,T &)”: 模板 参数“T”不明确

为了避免这个问题,一种解决方式就是增多模板参数

template <typename T1, typename T2>
void swap(T1& num1, T2& num2)
{
	T1 temp = num1;
	num1 = num2;
	num2 = temp;
}

int main()
{
	int a = 1;
	double b = 2.0;
	swap(a, b);

	return 0;
}

一种方法是使用强制类型转换,使传入的类型相同。但这个方法也有一个需要注意的点:如果函数模板的形参为引用类型,但是没有被const修饰,那么就不能用强制类型转换来实现推演实例化:

template <typename T>
void swap(T& num1, T& num2)
{
	T temp = num1;
	num1 = num2;
	num2 = temp;
}

int main()
{
	int a = 1;
	double b = 2.0;

	swap(a, (int)b);
 //(int)b涉及数据类型的转换,因此产生了一个临时变量,而临时变量具有常性(const),
 //被const修饰的引用权限不能被放大,因此无法转换为没有被const修饰的形参T& num

	return 0;
}

还有一种常用的方法就是使用显式实例化

2.3.2 显式实例化

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

注意:使用显式实例化同样需要注意引用权限不能提升的问题

例如:

template <typename T>
T Add(const T& num1, const T& num2)
{
	return num1 + num2;
}

int main()
{
	int a1 = 1, b1 = 2;
	double a2 = 2.0, b2 = 4.99;

	int ret1 = Add<int>(a1, a2);
	double ret2 = Add<double>(b1, b2);

	return 0;
}

2.3 类模板

在这里插入图片描述

基本格式

template<typename T1, typename T2,......,typename Tn>

class 类模板名 {};

例如:

//定义一个类模板stack
//该stack可以存储所有类型

template <class T>
class stack
{
public:
	stack(int capacity = 3)
	{
		_a = new T[capacity];
		_capacity = capacity;
		_top = 0;
	}

	void push(T val) {}

	//…………

private:
	T* _a;
	int _capacity;
	int _top;
};

类模板只能显示实例化,其基本格式为:

类模板名 <数据类型> 对象名

例如对于上面的类模板stack

stack<int> st1;
stack<double> st2;

成员函数声明和定义分离

template <class T>
class stack
{
public:
	stack(int capacity = 3)
	{
		_a = new T[capacity];
		_capacity = capacity;
		_top = 0;
	}

	void push(T val);

	//…………

private:
	T* _a;
	int _capacity;
	int _top;
};

//当声明和定义分离时,需要制定成员函数所在类的类型
//类模板的类模板名不是类名,类模板名<数据类型>才是类类型
//同样,成员函数的声明和定义不能分在两个不同的文件
template <class T>
void stack<T>::push(T val) {}

2.4 非类型模板参数

在这里插入图片描述

模板的参数列表支持传入非类型模板参数,以供在类模板或者函数模板的内部直接使用。

例如:

template<class T, size_t N = 10>
void func(T a)
{
	cout << N << endl;
}

int main()
{
	func(1);
	return 0;
}

output:

10

这里需要注意:

  • 非类型模板参数只能是整形数据,即size_t, int, char等类型
  • 非类型模板参数必须在编译时就能确定结果,因此只能是常量
  • 非类型模板参数只能是整型常量

2.5 模板的特化

有小伙伴会问:什么是模板的特化?

我们先来看下面的代码:

template<class T>
bool Less(T a, T b)
{
	return a < b;
}

int main()
{
	string str1 = "a";
	string str2 = "b";
	string* ptr1 = &str1;
	string* ptr2 = &str2;

	cout << Less(str1, str2) << endl;
	cout << Less(ptr1, ptr2) << endl;

	return 0;
}

output:

1
0
  • 我们定义了两个string对象:str1“a”和str2”b“,并用两个指针ptr1ptr2分别指向这两个对象

  • 我们调用函数模板Less欲比较对象str1str2以及指针ptr1ptr2所指向对象的大小

  • 比较str1str2时,字符串“a”确实小于字符串“b”,也得到了正确的结果:1

  • 比较ptr1ptr2时,确得到了相反的结果:0。这是为何?这是因为,我们传入函数模板的类型为string*,是一个指针类型,因此函数Less比较的也是两个指针的大小,而栈区的地址从高到低使用的,因此输出0

可见第二次比较与我们比较两指针指向的内容的本意不符,因此我们要对string*这一特殊情况进行处理,也就是模板的特化。

简单点来说,模板特化就是:

原模板类的基础上,针对特殊类型所进行特殊化的实现方式

模板的特化分为函数模板的特化以及类模板的特化

2.5.1 函数模板的特化

在这里插入图片描述

要进行函数模板的特化,首先必须要有一个基础的函数模板

特化的基本格式为:

  • template后面跟一个空的<>,即template<>
  • 函数名后面跟一个<>,里面为特化的具体类型
  • 函数的参数列表必须要和原函数模板的参数列表的格式一致

例如对于上面的Less,我们对类型string*进行特化:

template<class T>
bool Less(T a, T b)
{
	return a < b;
}

template<>
bool Less<string*>(string* a, string* b)
{
	return *a < *b;	//比较的是指针指向的内容
}

事实上,为了提高代码的可读性,如果我们要对函数参数的某一特定类型进行特殊处理,我们为往往会以函数重载的方式解决

bool Less(string* a, string* b)
{
	return *a < *b;
}

因此,函数模板的特化实际上并不常用

2.5.2 类模板的特化

在这里插入图片描述

类模板的特化分为全特化和偏特化两种:

2.5.2.1 全特化

全特化即是将模板参数列表中所有的参数都确定化

格式为:

  • template后面跟一个空的<>,即template<>
  • 类模板名后面跟一个<>,里面为特化的具体类型

例如:

template<class T1, class T2>
class A
{
public:
	void func()
	{
		cout << "A" << endl;
	}
};

template<>
class A<int, int>
{
public:
	void func()
	{
		cout << "A<int, int>" << endl;
	}
};
int main()
{
	A<int, int> a1;
	A<double, int> a2;
	a1.func();
	a2.func();

	return 0;
}

output:

A<int, int>
A
2.5.2.2 偏特化

全特化即是将模板参数列表中部分参数确定化

偏特化又分为两种,一种是确定部分参数的类型,一种是确定部分或所有参数的性质。我们分开来说明:

确定部分参数的类型

格式为:

  • template后面跟一个<>,里面为不要被确定的模板参数
  • 类模板名后面跟一个<>,里面为不要被确定的模板参数以及被确定的具体类型

例如:

template<class T1, class T2>
class A
{
public:
	void func()
	{
		cout << "A" << endl;
	}
};

template<class T1>
class A<T1, char>
{
public:
	void func()
	{
		cout << "A<T1, char>" << endl;
	}
};

template<class T2>
class A<double, T2>
{
public:
	void func()
	{
		cout << "A<double, T2>" << endl;
	}
};

确定部分或全部参数的性质

这里的性质指的就是指针或者引用或者普通类型

格式为:

  • template后面跟一个<>,里面的内容和原类模板一致
  • 类模板名后面跟一个<>,里面为确定的参数性质

例如:

template<class T>
struct Less
{
	bool operator() (T a, T b)
	{
		return a < b;
	}
};

//只要传入的类型为指针类型,那么就会用这个模板
template <class T>
struct Less<T*>
{
	bool operator() (T* a, T* b)
	{
		return *a < *b;
	}
};

3. 模板的本质

  • 和类实例化对象类似,我们不能将类看成是一个具体的对象,它只是一个不占据空间的蓝图

  • 同样,我们也不能将函数模板和类模板看成是一个具体的函数和类,他们也只是实例化一个具体函数和类的模具

  • 只有我们使用一个或多个具体的数据类型用模板实例化时,才会形成一个具体的函数和类。

在这里插入图片描述

如上图所示, 在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。因此实际上,我们没写的冗余代码,编译器都替我们完成了,是编译器在替我们负重前行。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

e5 服务器具备哪些性能特点?

随着云计算和大数据技术的不断发展&#xff0c;服务器作为数据中心的核心设备&#xff0c;其性能特点也日益受到关注。其中&#xff0c;E5服务器作为当前主流的服务器类型之一&#xff0c;具备许多优秀的性能特点。本文将详细介绍E5服务器的性能特点&#xff0c;帮助读者更好地…

【社交电商】带直播电商功能,可以DIY前端,可以H5和小程序一般商城常用功能齐全

第一次接触这个系统&#xff0c;感觉和微擎有点像。也是一个主体&#xff0c;也很多插件的那种。 测试了下。安装成功了&#xff0c;站长亲测没有问题&#xff0c;一切都挺完善的&#xff0c;不过系统比较庞大&#xff0c;可能新手熟悉起来要一定的过程。 站长整理了一份简要…

记录 | python list extend()

extend() 函数用于在列表末尾一次性追加另一个序列中的多个值&#xff08;用新列表扩展原来的列表&#xff09;。 以下实例展示了 extend()函数的使用方法&#xff1a; #!/usr/bin/pythonaList [123, xyz, zara, abc, 123]; bList [2009, manni]; aList.extend(bList)print …

Openwifi 开源项目解读(一)

Openwifi 是一个关于wifi 系统的开源项目&#xff0c;是一个少有的优秀的关于wifi的开源项目&#xff0c;项目中包括了wifi的基带、lowmac、linux驱动 等三部分&#xff0c;其中基带、lowmac部分是在FPGA中实现&#xff0c;wifi驱动部分是运行在Linux下&#xff0c;因此openwif…

【漏洞复现】SpringBlade export-user接口存在SQL注入漏洞

漏洞描述 SpringBlade 是一个由商业级项目升级优化而来的微服务架构 采用Spring Boot 2.7 、Spring Cloud 2021 等核心技术构建,完全遵循阿里巴巴编码规范。提供基于React和Vue的两个前端框架用于快速搭建企业级的SaaS多租户微服务平台。SpringBlade export-user接口存在SQL注…

Docker配置Portainer容器管理界面

目录 一、Portainer 简介 优点&#xff1a; 缺点&#xff1a; 二、环境配置 1. 拉取镜像 2. 创建启动容器 三、操作测试 1. 进入容器 2. 拉取镜像并部署 3. 访问测试 一、Portainer 简介 Portainer 是一个开源的轻量级容器管理界面&#xff0c;用于管理 Docker 容器…

使用yolo训练自己的模型

YOLO&#xff08;You Only Look Once&#xff09;是一种用于目标检测的深度学习模型&#xff0c;旨在实时检测图像或视频中的多个对象。与传统的目标检测方法不同&#xff0c;YOLO一次性处理整个图像&#xff0c;而不是通过滑动窗口或区域提议进行多次检测。这种方法使得YOLO在…

串的朴素模式匹配算法|小白入门详细讲解

字符串模式匹配&#xff1a;在主串中找到与模式串相同的子串&#xff0c;并返回其所在的位置 子串—主串 的一部分&#xff0c;一定存在模式串—不一定能在主串中找到 朴素模式匹配算法是一种暴力求解算法 在主串中找出所有可能与模式串相匹配的子串&#xff0c;将这些子串与…

自然语言处理(NLP)——使用Rasa创建聊天机器人

1 基本概念 1.1 自然语言处理的分类 IR-BOT&#xff1a;检索型问答系统 Task-bot&#xff1a;任务型对话系统 Chitchat-bot:闲聊系统 1.2 任务型对话Task-Bot:task-oriented bot 这张图展示了一个语音对话系统&#xff08;或聊天机器人&#xff09;的基本组成部分和它们之间的…

ChatGPT高效提问—prompt常见用法(续篇三)

ChatGPT高效提问—prompt常见用法&#xff08;续篇三&#xff09; 1.1 多选项 ​ 多选项技术为模型提供了一个清晰的问题或任务&#xff0c;并附带一组预先定义的潜在答案。这种方法在生成仅限于特定选项集的文本方面表现出色&#xff0c;适用于问答、文本补全和其他任务。利…

[VulnHub靶机渗透] Sar: 1

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

计网——运输层、端口号

目录 运输层 1 进程之间的通信 运输层的作用 屏蔽作用 可靠信道与不可靠信道 2 运输层的两个主要协议 3 运输层的端口 端口号 (protocol port number) 软件端口 硬件端口 TCP/IP 运输层端口的标志 两大类、三种类型的端口 常用的熟知端口 运输层 1 进程之间的通信 …

RabbitMQ(保姆级教程)

RabbitMQ学习 基础 1. 同步通信和异步通信 同步调用 下一步动作必须依赖上一步 异步调用 通知到位就行&#xff0c;不对消费者做强制要求&#xff0c;只要求最终一致性就行 2. MQ技术选项 消息先进先出&#xff0c;RabbitMQ默认有序 Erlang 是面向并发&#xff0c…

简化版SpringMVC

简化版SpringMVC web.xml xml version"1.0" encoding"UTF-8"?> <web-app version"2.5" xmlns"http://java.sun.com/xml/ns/javaee" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation&quo…

Go语言每日一题——链表篇(七)

传送门 牛客面试笔试必刷101题 ----------------删除链表的倒数第n个节点 题目以及解析 题目 解题代码及解析 解析 这一道题与昨天的题目在解题思路上有一定的相似之处&#xff0c;都是基于双指针定义快慢指针&#xff0c;这里我们让快指针先走n步&#xff0c;又因为n一定…

计算机网络-无线通信技术与原理

一般我们网络工程师接触比较多的是交换机、路由器&#xff0c;很少涉及到WiFi和无线设置&#xff0c;但是呢在实际工作中一般企业也是有这些需求的&#xff0c;这就需要我们对于无线的一些基本配置也要有独立部署能力&#xff0c;今天来简单了解一下。 一、无线网络基础 1.1 无…

[BUUCTF]-PWN:[极客大挑战 2019]Not Bad解析

保护 ida 这里使用mmap函数创造了一个内存映射区域 从地址0x123000开始&#xff0c;大小位0x1000 权限为可写可执行&#xff08;可读0x1&#xff0c;可写0x2&#xff0c;可执行0x3&#xff09; 设置为私有映射&#xff08;MAP_PRIVATE&#xff09;和匿名映射&#xff08;MAP…

Bootstrap5 响应式导航栏

Bootstrap5 响应式导航栏 我们可以使用 Bootstrap5 导航栏组件为网站或应用程序创建响应式导航标题。 这些响应式导航栏在手机等小视口的设备上会折叠&#xff0c;但当用户单击切换按钮时会展开。 但是&#xff0c;它在中型和大型设备&#xff08;例如笔记本电脑或台式机&#…

CPP项目:Boost搜索引擎

1.项目背景 对于Boost库来说&#xff0c;它是没有搜索功能的&#xff0c;所以我们可以实现一个Boost搜索引擎来实现一个简单的搜索功能&#xff0c;可以更快速的实现Boost库的查找&#xff0c;在这里&#xff0c;我们实现的是站内搜索&#xff0c;而不是全网搜索。 2.对于搜索…

2023年ABC123公众号年刊下载(PDF电子书)

Part1 前言 大家好&#xff0c;我是ABC_123。2023年公众号正式更名为"希潭实验室"。除了分享日常红队攻防、渗透测试技术文章之外&#xff0c;重点加强了APT案例分析方面的内容。公众号关注度得到进一步提升&#xff0c;关注人数已达到3万5千人。原计划在2023年编写…