C++模版(基础)

目录

C++泛型编程思想

C++模版

模版介绍

模版使用

函数模版

函数模版基础语法

函数模版原理

函数模版实例化

模版参数匹配规则

类模版

类模版基础语法


C++泛型编程思想

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

模板是泛型编程的基础。

虽然可以直接使用函数重载来解决不同类型的问题,但是使用函数重载会出现可能不好的地方

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

C++模版

模版介绍

在C语言中,当需要交换两个变量的数据时需要考虑到不同类型,例如下面的代码

#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>//交换int类型数据
void swap_int(int* num1, int* num2)
{int tmp = *num1;*num1 = *num2;*num2 = tmp;
}//交换double类型的数据
void swap_double(double* num1, double* num2)
{double tmp = *num1;*num1 = *num2;*num2 = tmp;
}int main()
{int num1 = 1, num2 = 2;printf("num1=%d num2=%d\n", num1, num2);swap_int(&num1, &num2);printf("num1=%d num2=%d\n", num1, num2);double num3 = 4.1, num4 = 4.5;printf("num3=%.1f num4=%.1f\n", num3, num4);swap_double(&num3, &num4);printf("num3=%.1f num4=%.1f\n", num3, num4);return 0;
}
输出结果:
num1=1 num2=2
num1=2 num2=1
num3=4.1 num4=4.5
num3=4.5 num4=4.1

在上面的C语言代码中,当需要交换int类型的数据时需要int类型交换函数,需要double类型的数据时需要double类型的交换函数,但是这两个函数除了类型不同以外其他代码都一样,增加了工作量,并且因为C语言不支持函数重载,所以两个交换函数的函数名不能相同

为了解决上面的问题,C++中提出了一种模版函数,如下面代码

#include <iostream>
using namespace std;template<class T>
void Swap(T& num1, T& num2)
{T tmp = num1;num1 = num2;num2 = tmp;
}int main()
{int num1 = 1, num2 = 2;printf("num1=%d num2=%d\n", num1, num2);Swap(num1, num2);printf("num1=%d num2=%d\n", num1, num2);double num3 = 4.1, num4 = 4.5;printf("num3=%.1f num4=%.1f\n", num3, num4);Swap(num3, num4);printf("num3=%.1f num4=%.1f\n", num3, num4);return 0;
}
输出结果:
num1=1 num2=2
num1=2 num2=1
num3=4.1 num4=4.5
num3=4.5 num4=4.1

在上面的代码中,将Swap函数作为一种模版,当调用Swap函数时,根据传入的参数类型自动实例化函数从而完成函数执行

模版使用

函数模版

函数模版基础语法
template<typename name1, typename name2, ...>
函数返回类型 函数名(形式参数)
{//函数体
}//typename也可以用class代替,但是不可以用struct

在C++中,使用template关键字创建模版,使用<>包裹函数体中需要使用到类型,typename name1用于指代的类型,在函数调用时自动匹配类型,默认不会隐式类型转换,模版下方正常写函数即可

📌

模版函数的下方也可以写其他普通函数

#include <iostream>
using namespace std;//模版
template<class T>
void Swap(T& num1, T& num2)
{T tmp = num1;num1 = num2;num2 = tmp;
}//普通函数
int add(const int num1, const int num2)
{return num1 + num2;
}int main()
{int num1 = 1, num2 = 2;printf("num1=%d num2=%d\n", num1, num2);Swap(num1, num2);printf("num1=%d num2=%d\n", num1, num2);double num3 = 4.1, num4 = 4.5;printf("num3=%.1f num4=%.1f\n", num3, num4);Swap(num3, num4);printf("num3=%.1f num4=%.1f\n", num3, num4);cout << add(num1, num2) << endl;//可以正常使用return 0;
}
输出结果:
num1=1 num2=2
num1=2 num2=1
num3=4.1 num4=4.5
num3=4.5 num4=4.1
3
函数模版原理

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

在函数调用的过程中,直接调试时不论是int类型还是double类型都会走到模版,但是进入反汇编可以看到当形参是int类型时,编译器会进入int类型的函数,同样double类型类似

所以,函数模版是告诉编译器应该生成何种类型的函数,如下图所示

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

函数模版实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。

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

隐式实例化:让编译器根据实参类型自动推演出形式参数类型

#include <iostream>
using namespace std;template<class T>
void Swap(T& num1, T& num2)
{T tmp = num1;num1 = num2;num2 = tmp;
}int main()
{int num1 = 1, num2 = 2;printf("num1=%d num2=%d\n", num1, num2);Swap(num1, num2);//自动推演出int类型printf("num1=%d num2=%d\n", num1, num2);return 0;
}
输出结果:
num1=1 num2=2
num1=2 num2=1

但是,当模版参数类型种类个数与实参种类个数不匹配时,编译器将无法自动推演

#include <iostream>
using namespace std;template<class T>
void add(T& num1, T& num2)
{return num1 + num2;
}int main()
{int num1 = 1;double num2 = 2.0;add(num1, num2);//无法自动推演return 0;
}
报错信息:
没有与参数列表匹配的 函数模板 "Swap" 实例

在上面的代码中,函数模版中只有一种类型,但是实际调用函数传递的实际参数对应两种类型,此时因为类型不对应编译报错

第一种解决方式:添加额外种类的模版参数

#include <iostream>
using namespace std;template<class T, class R>
T add(T& num1, R& num2)
{return num1 + num2;
}int main()
{int num1 = 1;double num2 = 2.0;cout << add(num1, num2) << endl;//当函数模版有两种参数时可以自动推演return 0;
}
输出结果:
3

在上面的代码中,类型T被推演为int,类型R被推演为double,但是有个返回值问题,因为函数返回值只能为一种,所以存在精度丢失

第二种解决方式:对某一种类型进行强制转换

以强制转换int类型为例

#include <iostream>
using namespace std;template<class T>
T add(T num1, T num2)
{return num1 + num2;
}int main()
{int num1 = 1;double num2 = 2.2;cout << add((double)num1, num2); << endl;//将int类型转换为double类型return 0;
}
输出结果:
3.2

第三种解决方式:显式实例化

#include <iostream>
using namespace std;template<class T>
T add(T num1, T num2)
{return num1 + num2;
}int main()
{int num1 = 1;double num2 = 2.2;cout << add<double>(num1, num2) << endl;//强制指定T为double类型此时会隐式转换return 0;
}
输出结果:
3.2

对于显式实例化来说,如果此时类型依旧不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错

📌

注意,第二种方式和第三种方式都有强制性,指定的类型时何种类型函数模版就一定是何种类型,哪怕函数模版的形参是同类型的引用编译器也无法识别

模版参数匹配规则
  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
#include <iostream>
using namespace std;
//同名函数模版和非模版函数
//函数模版
template<class T, class R>
R add(T num1, R num2)
{return num1 + num2;
}//单独处理整型加法
int add(int num1, int num2)
{return num1 + num2;
}int main()
{int num1 = 1;int num2 = 2;cout << add(num1, num2) << endl;//此时编译器会调用单独处理整型加法的函数,而不是根据函数模版推演出新的int形参函数double num3 = 2.2;cout << add(num1, num3) << endl;//编译器直接推演出不需要强制转换的函数return 0;
}
输出结果:
3
3.2

类模版

类模版基础语法
template<typename name1, typename name2>
class 类名
{//类体
};

在C++中,使用template关键字创建模版,使用<>包裹类体体中需要使用到的类型,typename name1用于指代类型,在使用类时自动匹配数据类型,默认不会隐式类型转换,模版下方正常写类即可

📌

注意,使用模版类创建类对象时必须显式指定类型

#include <iostream>
using namespace std;template<class T>
class SeqList
{    
private:T* _a;int _size;int _capacity = 4;
public:SeqList():_a(nullptr){_a = new T[_capacity];}~SeqList(){delete[] _a;_size = _capacity = 0;}
};int main()
{//类模版必须显式制定类型SeqList<int> s1;//存放int类型数据的顺序表SeqList<double> s2;//存放double类型的顺序表return 0;
}

📌

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

例如上面的代码中有两个类,一个是SeqList<int>,一个是SeqList<double>

如果声明和定义分开时,域作用限定符左侧的域名一定要带上模版参数列表

SeqList<int>::~SeqList()
{delete[] _a;_size = _capacity = 0;
}SeqList<double>::~SeqList()
{delete[] _a;_size = _capacity = 0;
}

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

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

相关文章

【前端Vue】Vue3+Pinia小兔鲜电商项目第3篇:静态结构搭建和分类实现,1. 整体结构创建【附代码文档】

Vue3ElementPlusPinia开发小兔鲜电商项目完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;认识Vue3&#xff0c;使用create-vue搭建Vue3项目1. Vue3组合式API体验,2. Vue3更多的优势,1. 认识create-vue,2. 使用create-vue创建项目,1. setup选项的写法和执行…

【数据结构与算法】java有向带权图最短路径算法-Dijkstra算法(通俗易懂)

目录 一、什么是Dijkstra算法二、算法基本步骤三、java代码四、拓展&#xff08;无向图的Dijkstra算法&#xff09; 一、什么是Dijkstra算法 Dijkstra算法的核心思想是通过逐步逼近的方式&#xff0c;找出从起点到图中其他所有节点的最短路径。算法的基本步骤如下&#xff1a;…

应用层协议 - HTTP

文章目录 目录 文章目录 前言 1 . 应用层概要 2. WWW 2.1 互联网的蓬勃发展 2.2 WWW基本概念 2.3 URI 3 . HTTP 3.1 工作过程 3.2 HTTP协议格式 3.3 HTTP请求 3.3.1 URL基本格式 3.3.2 认识方法 get方法 post方法 其他方法 3.3.2 认识请求报头 3.3.3 认识请…

MyBatis是纸老虎吗?(七)

在上篇文章中&#xff0c;我们对照手动编写jdbc的开发流程&#xff0c;对MyBatis进行了梳理。通过这次梳理我们发现了一些之前文章中从未见过的新知识&#xff0c;譬如BoundSql等。本节我想继续MyBatis这个主题&#xff0c;并探索一下MyBatis中的缓存机制。在正式开始梳理前&am…

如何解决kafka rebalance导致的暂时性不能消费数据问题

文章目录 背景思考答案排它故障转移共享 背景 之前在review同组其它业务的时候&#xff0c;发现竟然把kafka去掉了&#xff0c;问了下原因&#xff0c;有一个单独的服务&#xff0c;我们可以把它称为agent&#xff0c;就是这个服务是动态扩缩容的&#xff0c;会采集一些指标&a…

使用C++实现一个简单的日志功能

日志对于一些大一些的项目来说&#xff0c;可以在项目运行出现问题时更好的帮助 项目的维护人员快速的定位到问题出现的地方并且知道出现问题的原因&#xff0c; 并且日志也可以帮助程序员很好的进行项目的Debug&#xff0c;那么今天我就来实 现一个C编写的一个简单的日志功能。…

深度学习中常用计算距离的几种算法对比与python实现

前言 距离度量在许多机器学习算法中扮演着至关重要的角色&#xff0c;无论是监督学习还是无监督学习。选择适当的距离度量可以显著影响模型的性能。 在高维数据集中&#xff0c;欧几里得距离可能会受到所谓的“维度诅咒”的影响&#xff0c;因为随着维度的增加&#xff0c;数…

海外媒体软文发稿:谷歌关键词优化细分人群成功案例,突破海外市场!

海外媒体软文发稿&#xff1a;谷歌关键词优化细分人群成功案例&#xff0c;突破海外市场&#xff01; 引言 在全球化的时代&#xff0c;海外市场对于企业的发展至关重要。而在海外市场中&#xff0c;互联网媒体的作用不可忽视。本篇教程将介绍如何通过谷歌关键词优化细分人群…

Spring设计模式-实战篇之单例模式

实现案例&#xff0c;饿汉式 Double-Check机制 synchronized锁 /*** 以饿汉式为例* 使用Double-Check保证线程安全*/ public class Singleton {// 使用volatile保证多线程同一属性的可见性和指令重排序private static volatile Singleton instance;public static Singleton …

Learn OpenGL 30 SSAO

SSAO 我们已经在前面的基础教程中简单介绍到了这部分内容&#xff1a;环境光照(Ambient Lighting)。环境光照是我们加入场景总体光照中的一个固定光照常量&#xff0c;它被用来模拟光的散射(Scattering)。在现实中&#xff0c;光线会以任意方向散射&#xff0c;它的强度是会一…

C++ STL - 优先级队列及其模拟实现

目录 0. 引言 1. priority_queue 介绍 1.1 构造函数 1.2 priority_queue 接口函数使用 1.3 仿函数 1.4 题目练习 2. priority_queue 模拟实现 2.1基本框架&#xff1a; 2.2 默认构造函数 2.3 基本函数 2.4 堆的向上以及向下调整 0. 引言 优先队列 (priority_queu…

【剑指offr--C/C++】JZ22 链表中倒数最后k个结点

一、题目 二、思路及代码 遍历链表并存入vector容器&#xff0c;通过下标取出对应位置元素或者返回空 /*** struct ListNode {* int val;* struct ListNode *next;* ListNode(int x) : val(x), next(nullptr) {}* };*/ #include <cstddef> #include <iterator> #…

【微服务】接口幂等性常用解决方案

一、前言 在微服务开发中&#xff0c;接口幂等性问题是一个常见却容易被忽视的问题&#xff0c;同时对于微服务架构设计来讲&#xff0c;好的幂等性设计方案可以让程序更好的应对一些高并发场景下的数据一致性问题。 二、幂等性介绍 2.1 什么是幂等性 通常我们说的幂等性&…

【Entity Framework】 EF三种开发模式

【Entity Framework】 EF三种开发模式 文章目录 【Entity Framework】 EF三种开发模式一、概述二、DataBase First2.1 DataBase First简介2.2 DataBase First应用步骤2.3 DataBase First总结 三、Model First3.1 Model First简介3.2 Model First实现步骤 四、Code First4.1 Cod…

全市场都忽视了!大摩:数据中心算力成本正在迅速下降

随着AI技术迈入普及与应用的新纪元&#xff0c;其算力需求蓬勃增长的同时&#xff0c;算力成本随技术进步呈现下降趋势。 摩根士丹利在3月24日的AI报告中指出&#xff0c;随着GPU技术的不断进步&#xff0c;例如英伟达从Hopper发展到Blackwell GPU架构&#xff0c;GAI的算力成…

ChatGPT 对 ELT的理解

本文主要内容来自 ChatGPT 4.0 到底什么是 ETL&#xff1f;在数据库内部&#xff0c;把数据从 ODS 层加工成 DWD&#xff0c;再加工成 DWS&#xff0c;这个过程和 ETL 的关系是什么&#xff1f;带着这些问题&#xff0c;我问了一下 ChatGPT&#xff0c;总结如下。 数据在两个数…

AWS EC2设置root登录

在使用亚马逊的服务器时&#xff0c;官方默认是使用密钥登录&#xff0c;跟国内的云服务器差别较大&#xff0c;本文记录下&#xff0c;如何开放AWS EC2的root登录。 一、通过网页版或者XShell登录服务器 这里略过 二、设置root账户密码 # 切换 root sudo -i # 设置或修改密…

静态住宅IP VS 动态住宅IP,怎么选择?

在进行海外 IP 代理时&#xff0c;了解动态住宅 IP 和静态住宅 IP 的区别以及如何选择合适的类型非常重要。本文将介绍精态住宅 IP 特点和&#xff0c;并提供选择建议&#xff0c;帮助您根据需求做出明智的决策。 静态住宅 IP 的特点 静态住宅 IP 是指 IP 地址在一段时间内保…

故障诊断 | 一文解决,CNN-BiLSTM卷积神经网络-双向长短期记忆神经网络组合模型的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | 一文解决,CNN-BiLSTM卷积神经网络-双向长短期记忆神经网络组合模型的故障诊断(Matlab) 模型描述 CNN-BiLSTM卷积神经网络-双向长短期记忆神经网络组合模型是一种深度学习模型,结合了卷积神经网络(CNN)和双向长短期记忆网络(BiLSTM)的优点…

CBLUEbenchmark代码跑通

CBLUEbenchmark 跑通 github源地址 1.配置 数据下载 新建文件夹CLBUEDatasets,将数据放在该文件夹下模型下载到github源地址中选择一个模型来下载: 将下载好的模型放在一个专门放模型的文件夹下&#xff0c;我这里是bert 文件如下所示放置&#xff1a; 2.训练模型&#xff…