【C++入门】函数模板类模板(泛型编程)

news2024/11/25 12:48:55

目录

前言

 1. 泛型编程

 2. 函数模板

 2.1 概念

 2.2  语法格式

 2.3 原理

 2.4 函数模板的实例化

 隐式实例化

 显示实例化

  2.5 思考

  2.6 模板参数的匹配原则

 3. 类模板

 3.1 类模板的定义格式

 3.2 类模板的实例化

总结


前言

        函数模板和类模板是C++中的两种重要的模板形式,本文将系统的介绍函数模板和类模板这两个基本概念,以及它们在C++编程中的基本应用和使用方法。

在这里插入图片描述

 1. 泛型编程

  先来看一个例子:

如何实现一个通用的函数(可应对任何类型的参数)?

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}
void Swap(char& left, char& right)
{
	char temp = left;
	left = right;
	right = temp;
}
// ...

 以Swap函数为例,使用函数重载确实可以达到应对任何类型的参数,但是你会发现它的缺陷:

  • 代码重复率很高,逻辑都是一样的
  • 代码的可维护性比较低,一个出错可能所有的重载均出错

 为了解决这个问题,在C++中,存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码)。

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础

模板主要分为两类:

 

 2. 函数模板

     2.1 概念

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

    2.2  语法格式

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

void functionName(T1 param1, T2 param2, ...) 
{
    // 函数实现
}

 可以根据函数参数的类型种类来添加模板参数

 把 typename 换成 class 也可以的(不能使用struct代替class)

 使用示例:

template<class T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

 2.3 原理

    我们尝试使用上述的Swap函数模板,交换两种不同类型的数据,两次调用的函数是否是同一个函数?

double a1 = 3.14, a2 = 12.56;
int b1 = 3, b2 = 6;
Swap(a1, a2);
Swap(b1, b2);

 它们调用的不是同一个函数

         在现在新版编译器的调试下,我们看到的是都调用了函数模板,看似是调用了同一个函数,但实际上不是。

 转到底层汇编,它们调用的不是同一个函数.

 函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。   所以其实模板就是将本来应该我们做的重复的事情交给了编译器

 

 在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型   的函数以供调用

比如:double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然
后产生一份专门处理double类型的代码

 2.4 函数模板的实例化

 编译器根据实际的模板参数类型生成具体的函数实现的过程叫函数模板的实例化

 模板参数实例化分为:隐式实例化和显式实例化

 隐式实例化

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

 比如:

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}


int main()
{
    int a1 = 10, a2 = 20;
    double d1 = 10.0, d2 = 20.0;

    Add(a1, a2);
    Add(d1, d2);

    return 0;
}

在模板中,编译器一般不会进行类型转换操作

 当只有一个模板参数时,编译器无法确定数据类型。

两种解法:

  • 用户自己来强制转化
Add(a1, (int)d1);
  •  使用显式实例化
 显示实例化

 在函数名后的<>中指定模板参数的实际类型

Add<int>(a1, d2);

 应用场景:

template<class T>
T* fun()
{
	T* p = new[10];
	return p;
}
int main()
{

	//fun(); //无法通过编译:无法推导“T”的 模板 参数
	return 0;
}

 这时候就只能使用显示实例化:

int* p = fun<int>();

 如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错

 显示实例化与隐式实例化在使用时应根据情况使用(并不常用)。

 2.5 思考

 显示实例化与隐式实例化适用于所有的模板函数吗?

不一定有些函数模板中就无法使用比如Swap函数模板:

template<class T>
void Swap(T& left,T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

int main()
{
	int b1 = 3, b2 = 6;
	
	double a1 = 3.14, a2 = 12.56;
	
	char ch1 = 'a', ch2 = 'b';
	
    Swap((int)a1, b2); //无法通过编译

	return 0;
}

         为什么无法通过?看着似乎没什么问题,但是好好观察思考一下你就会发现问题所在,强制类型转换中间会产生临时值,降临时值传入到函数模板中,问题就出在这里;

临时值具有常属性,而函数模板中没有const修饰,导致了权限放大,加上const就行了吗?

加上const就产生了新问题:修饰后的形参具有了常属性就无法再赋值了。

 所以Swap函数没法使用强转和显式实例化

2.6 模板参数的匹配原则

  •  非模板函数可以和一个同名的函数模板同时存在,并且该函数模板还可以被实例化为这个非     模板函数
  • 非模板函数和同名函数模板,如果同时存在并且参数类型两种都符合,在调动时会优先调用  非模板函数,如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
  •  模板函数不允许自动类型转换

 3. 类模板

 3.1 类模板的定义格式

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

比如:

template<class T>
class Stack
{
public:
	Stack(int n = 4);
	{
		cout << "Stack(int n = 4)" << endl;

		_a = new T[n];
		_top = 0;
		_capacity = n;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;

		delete[] _a;
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(const T& x)
	{
		//...
	}

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

 使用意义:类模板的使用可以存储各种类型的数据。

在C语言中我们使用typedef重命名便于修改不同的数据类型,但是应对以下情况较为复杂:

Stack st1; // 存int类型数据
Stack st2; // 存double类型数据

 要想在定义一个存储double类型数据就需要再写一个几乎一模一样的栈。代码复用率太低。

 使用注意:

  • 类模板成员函数和类不能分离(放在两个文件里),会出现链接错误
  • 类模板实例化的类:类名不是类型,类名<数据类型>才是整个类的类型
  • 显示实例化的类型不同,他们就是不同的类

 类模板声明定义分离(类中声明,类外定义,在同一文件中)

 示例:

template<class T>
class Stack
{
public:
	Stack(int n = 4); // 类中声明,类外定义
	~Stack()
	{
		cout << "~Stack()" << endl;

		delete[] _a;
		_a = nullptr;
		_top = _capacity = 0;
	}

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

template<class T>
Stack<T>::Stack(int n)
{
	cout << "Stack(int n = 4)" << endl;

	_a = new T[n];
	_top = 0;
	_capacity = n;
}

 注意:类模板中函数放在类外进行定义时,需要加模板参数列表

3.2 类模板的实例化

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

Stack<int> st1;
Stack<double> st2;

Stack是类名,Stack<int>才是类型


总结

        以上便是本文的全部内容, 本文主要介绍了一些C++模板的基本使用,以及一些注意事项,希望对你有所帮助,感谢阅读!

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

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

相关文章

day11 有效的括号 删除字符串中的所有相邻重复项 逆波兰表达式求值

题目1&#xff1a;20 有效的括号 题目链接&#xff1a;20 有效的括号 题意 判断字符串是否有效&#xff0c;若有效&#xff1a; 1&#xff09;左括号必须用相应的右括号 2&#xff09;左括号的闭合顺序正确 ({)}顺序不正确&#xff0c;应该是&#xff08;{}&#xff09; …

深度解析Nginx负载均衡算法及配置实例

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

Python3从零基础到入门(1)

目录 一、环境搭建 1.检测Python环境 2.下载安装Python环境 3.VSCode中配置Python环境 二、第一个程序 1.编码 2.输出 3.标识符 4.import 5.保留字 6.注释 7.缩进 三、变量和赋值 1.Python 中的变量 2.变量的赋值 3.多个变量赋值 四、基础数据类型 1.类型查看…

智能音箱喇叭杂音问题

智能音箱喇叭杂音问题 智能音箱生厂或出货过程会遇到多种喇叭播放有杂音的问题&#xff0e; 螺丝不匹配 智能音箱设备在生产过程&#xff0c;会有SPL测试喇叭失真&#xff0c;发现不良率8%的杂音问题&#xff0e; 分析原因是来料导入了新螺丝&#xff0c; 使用过程进入异物…

harmonyOS 时间选择组件(TimePicker)

本文 我们来说 TimePicker 时间组件 首先 我们搭一个最基本的组件骨架 Entry Component struct Index {build() {Row() {Column() {}.width(100%)}.height(100%)} }然后 在 Column 组件内 放一个 TimePicker进去 这里 我们就可以看到 一个时间的选择器了 DatePicker 捕获当前…

EPQ艾森克人格测试 49题(免费版)

艾森克人格提出人格的三个基本因素&#xff1a;性格内外向E、神经质N&#xff08;也叫情绪性&#xff09;和精神质P。这三个维度的不同程度组合&#xff0c;形成了独立的个体人格特征。 其中性格内外向维度是目前评估性格内向和外向的成熟量表&#xff0c;神经质和精神质为人格…

机器人技能学习-robosuite-0-入门介绍

文章目录 前言模块介绍实战案例1&#xff1a;从 demo 中创建自己的 env案例2&#xff1a;更换属于自己的物体 前言 资料太少、资料太少、资料太少&#xff0c;重要的事说三边&#xff0c;想根据自己实际场景自定义下机器人&#xff0c;结果发现无路可走&#xff0c;鉴于缺少参…

Maven初学Day1

1.Maven是什么&#xff1f; 是一个构建工具&#xff0c;可以自动化构建过程、任务。是一个项目模型 2.作为构建工具有什么特点 1.跨平台&#xff0c;可以在多个操作系统上使用 2.对外提供一致的操作接口 3.Maven官网 https://maven.apache.org/download.cgi 4.安装步骤 …

卷积神经网络|迁移学习-猫狗分类完整代码实现

还记得这篇文章吗&#xff1f;迁移学习|代码实现 在这篇文章中&#xff0c;我们知道了在构建模型时&#xff0c;可以借助一些非常有名的模型&#xff0c;这些模型在ImageNet数据集上早已经得到了检验。 同时torchvision模块也提供了预训练好的模型。我们只需稍作修改&#xf…

外延炉及其相关的小知识

外延炉是一种用于生产半导体材料的设备&#xff0c;其工作原理是在高温高压环境下将半导体材料沉积在衬底上。 硅外延生长&#xff0c;是在具有一定晶向的硅单晶衬底上&#xff0c;生长一层具有和衬底相同晶向的电阻率且厚度不同的晶格结构完整性好的晶体。 外延生长的特点&…

Linux实验——页面置换算法模拟

页面置换算法模拟 【实验目的】 &#xff08;1&#xff09;理解虚拟内存管理的原理和技术。 &#xff08;2&#xff09;掌握请求分页存储管理的思想。 &#xff08;3&#xff09;理解常用页面置换算法的思想。 【实验原理/实验基础知识】 存储器是计算机系统的重要资源之…

腾讯面试总结

腾讯 一面 mysql索引结构&#xff1f;redis持久化策略&#xff1f;zookeeper节点类型说一下&#xff1b;zookeeper选举机制&#xff1f;zookeeper主节点故障&#xff0c;如何重新选举&#xff1f;syn机制&#xff1f;线程池的核心参数&#xff1b;threadlocal的实现&#xff…

揭开JavaScript数据类型的神秘面纱

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》 ​ ​ ✨ 前言 JavaScript作为一门动态类型语言,其数据类型一直是开发者们关注的话题。本文将深入探讨Jav…

C语言算法(二分查找、文件读写)

二分查找 前提条件&#xff1a;数据有序&#xff0c;随机访问 #include <stdio.h>int binary_search(int arr[],int n,int key);int main(void) {}int search(int arr[],int left,int right,int key) {//边界条件if(left > right) return -1;//int mid (left righ…

MidTool的AIGC与NFT的结合-艺术创作和版权保护的革新

在数字艺术和区块链技术的交汇点上&#xff0c;NFT&#xff08;非同质化代币&#xff09;正以其独特的方式重塑艺术品的收藏与交易。将MidTool&#xff08;https://www.aimidtool.com/&#xff09;的AIGC&#xff08;人工智能生成内容&#xff09;创作的图片转为NFT&#xff0c…

数据库基础知识1

目录 数据库的使用 登录mysql 命令语法 常用命令 ​编辑 navicat建立连接 mysql授权管理命令 ​编辑mysql权限 数据导入导出 实例 数据导出 未登录 已经登录 导出导入的代码对比 ​编辑 导入导出的一个坑 python的导入导出 数据库基础知识 特点 需要掌握的程…

嵌入式——循环队列

循环队列 (Circular Queue) 是一种数据结构(或称环形队列、圆形队列)。它类似于普通队列,但是在循环队列中,当队列尾部到达数组的末尾时,它会从数组的开头重新开始。这种数据结构通常用于需要固定大小的队列,例如计算机内存中的缓冲区。循环队列可以通过数组或链表实现,…

使用Docker-compose快速构建Nacos服务

在微服务架构中&#xff0c;服务的注册与发现扮演着至关重要的角色。Nacos&#xff08;Naming and Configuration Service&#xff09;是阿里巴巴开源的服务注册与发现组件&#xff0c;致力于支持动态配置管理和服务发现。最近&#xff0c;一位朋友表达了对搭建一套Nacos开发环…

速卖通店铺销量飙升:掌握自养号测评(补单),轻松提升销售量

很多卖家在经营速卖通店铺时&#xff0c;都希望能提高自己店铺的曝光率。但对于一些新手卖家来说&#xff0c;可能不太清楚曝光率的具体含义以及如何提升。那么&#xff0c;让我们一起来探讨一下这个问题。 曝光率&#xff0c;简而言之&#xff0c;是指您的店铺和产品展示给顾…

springboot git配置文件自动刷新失败问题排查

http://{ip}:{port}/refresh 说明&#xff1a;springBoot版本是1.5.9&#xff0c;接口路径与2.x&#xff0c;不同 路径区别&#xff1a;/refresh VS /actuator/refresh 用postman调用refresh接口刷新git配置&#xff0c;报错如下&#xff0c;没有权限 在服务本地启动&#…