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

C++ 函数模板和类模板

参考视频:C++类模板_哔哩哔哩_bilibili

遗留问题:编译器怎么处理函数模板和类模板

目录

一、为什么会有函数模版?函数模板是为了解决什么问题?

二、函数模板的概念

三、函数模版的使用

四、函数模板的特化

五、类模板的概念

六、类模板的使用

七、类模板的特化

八、类模板的偏特化


一、为什么会有函数模版?函数模板是为了解决什么问题?

     函数模板是为了减少重复代码,是整合多个函数的函数体相同而区别只在参数类型不同的代码冗余的情况。不用函数模板前,多个函数的参数类型不同,但函数体相同,导致重复代码较多。有了函数模板,只需要一份函数体一份代码就可以实现原来需要几个函数体才实现的功能,例如不使用函数模板时,要分别对int、char等类型的两个数据做是否相等判断,需要定义多个函数:

bool isEqual( int nA,  int nB)
{return nA == nB;
}bool isEqual(char cA, char cB)
{return cA == cB;
}int main()
{int nA = 10;int nB = 20;char cA = 'A';char cB = 'B';std::cout << isEqual(nA, nB)<< std::endl;std::cout << isEqual(cA, cB) << std::endl;return 0;
}

 可见重载的两个函数的函数体实现一样,函数体代码重复,下面是使用函数模板的代码:

#include <iostream>
//使用模板代替原来的多个函数
template<typename T>
bool isEqual(T tA, T tB)
{return tA == tB;
}int main()
{
//变量以匈牙利命名法命名int nA = 10;int nB = 20;char cA = 'A';char cB = 'B';
//编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(int, int);std::cout << isEqual(nA, nB)<< std::endl;
//编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(char, char);std::cout << isEqual(cA, cB) << std::endl;return 0;
}

二、函数模板的概念

       函数模板是用于生成函数的模板。在编译阶段,编译器会根据函数模板的使用情况(例如上面的isEqual(nA, nB)、isEqual(cA, cB)创建出函数名相同,参数类型由编译器判断的若干函数。通过函数模板创建的函数拥有相同的函数体,只是函数的参数类型不同。

三、函数模版的使用

        每当在一个编译单元(经过预处理的.cpp文件)中使用了函数模版,则必须在该编译单元中给出函数模版的定义(要是把函数模板声明写在.h文件,函数模板声明写在.cpp文件,然后在使用函数模板时只使用include 包含函数模板的头文件,不给出函数模板的定义,那么编译会报无法解析的外部符号错误)。因此,为了避免在每个编译单元都定义函数模板,建议在头文件中对函数模板进行声明定义(即将函数模板的声明和定义都写在一个头文件中,然后在需要使用函数模板的地方加上该头文件)。

   函数模版的声明:

template<typename T>返回类型 函数名(参数列表);其中T表示任意类型,参数的类型和返回类型都可以指定为T

函数模版的定义

template<typename T,..>  //可指定多个泛型
返回类型 函数名(参数列表)
{//函数体
}

将一、为什么会有函数模版?函数模板是为了解决什么问题?这一节中的函数模板的声明和定义写在isEqualFunc.h头文件中再使用:

#ifndef ISEQUALFUNC_H
#define ISEQUALFUNC_H
//函数模板声明
template<typename T>
bool isEqual(T tA, T tB);//函数模板定义
template<typename T>
bool isEqual(T tA, T tB)
{return tA == tB;
}#endif // ISEQUALFUNC_H
//这是main.cpp
#include "isEqualFunc.h"
#include <iostream>int main()
{//变量以匈牙利命名法命名int nA = 100111111;int nB = 111111;char cA = 'A';char cB = 'A';//编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(int, int);std::cout << isEqual(nA, nB)<< std::endl;//编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(char, char);std::cout << isEqual(cA, cB) << std::endl;return 0;
}

四、函数模板的特化

函数模板特化是指在实例化函数模板时,对特定类型的实参进行特殊的处理,即当实参为特定类型时,通过函数模板生成的函数会有不同的函数体。

特化时需要为函数模板添加新的定义,方式如下:

template<>
返回类型 函数名<特定的参数类型>(参数列表)
{//函数体
}

在上面的例子中,对于char*类型的字符串,使用上面的函数模板无法对字符串正确判断是否相等,因为字符串比较不是直接比较指针是否相等,而要调用strcmp函数判断,对上面的例子就需要对函数模板进行特化

#ifndef ISEQUALFUNC_H
#define ISEQUALFUNC_H
#include <string.h>
//函数模板声明
template<typename T>
bool isEqual(T tA, T tB);//函数模板定义
template<typename T>
bool isEqual(T tA, T tB)
{return tA == tB;
}//函数模板的特化
template<>
bool isEqual< char* >( char* szA,  char* szB)
{return strcmp(szA, szB) == 0;
}#endif // ISEQUALFUNC_H
//这是main.cpp
#include "isEqualFunc.h"
#include <iostream>int main()
{//变量以匈牙利命名法命名int nA = 100111111;int nB = 111111;char cA = 'A';char cB = 'A';char szA[4] = "AAA";char szB[4] = "AAA";// 编译器会怎么处理函数模板和特化?待参考https://blog.csdn.net/zgaoq/article/details/85232968//编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(int, int);std::cout << isEqual(nA, nB)<< std::endl;//编译期间,编译器根据实参类型和模板函数生成函数bool isEqual(char, char);std::cout << isEqual(cA, cB) << std::endl;std::cout << isEqual(szA, szB) << std::endl; //判断字符串是否相等return 0;
}

五、类模板的概念

类模板是用于生成类的模板。在编译阶段,编译器会根据类模板的使用情况创建出仅部分成员数据类型和部分成员函数的参数类型不同,其他完全相同的若干类。

通过类模板的这些特性我们可以尝试写出用于存放不同类型数据的容器。

六、类模板的使用

类模板的声明格式:

template<typename T,...>
class 类名
{//成员
};

类模板的成员函数定义如下,一般类模板的定义也应该写在头文件中: 

template<typename T,...>
返回类型 类名<T,...>::成员函数名(形参列表)
{//函数体
}

下面是一个例子,这个例子定义了一个MyArray的类模板,其中数据成员是数组,通过泛型T达到可定义各种类型数组的目的,而所有对这个数组的操作函数都相同,这样就不用针对不同类型的数组,定义不同的类,达到了复用代码即减少重复代码的目的:

//这是myArray.h
#ifndef MYARRAY_H
#define MYARRAY_H
#include <iostream>
constexpr int m_nMaxLen = 20;template <typename T>
class MyArray
{
public:MyArray():size(0){}//添加数据bool addData(T value);//删除数据bool deleteData(T value);//获取数据T getData(int nIndex);void print(){for(int i = 0; i < size; i++){std::cout << data[i] << std::endl;}}
private:T data[m_nMaxLen];int size;
};template<typename T>
bool MyArray<T>::addData(T value)
{if(size == m_nMaxLen){return false;}data[size] = value;size++;return true;
}template<typename T>
bool MyArray<T>::deleteData(T value)
{if(size == 0){return false;}int nDataIndex = -1;for(int i = 0; i < size; i++){if(data[i] == value){nDataIndex = i;break;}}if(nDataIndex == -1){std::cout<< "找不到该数据!"  << std::endl;return false;}else{for(int i = nDataIndex; i < size -1; i++){data[i] = data[i+1];  //往前移动后面的数据,即可以删除该数据}size --;}return true;
}template<typename T>
T  MyArray<T>::getData(int nIndex)
{if(nIndex >= 0 && nIndex < size){return data[nIndex];}else{return (T)0;}
}#endif // MYARRAY_H
//这是main.cpp
#include "myarray.h"
#include <iostream>
int main()
{std::cout <<"----int begin----" << std::endl;MyArray<int> intData;for(int i = 1; i <= 10; i++){intData.addData(i);}intData.print();std::cout <<"--------" << std::endl;intData.deleteData(11);std::cout <<"--------" << std::endl;intData.print();std::cout << intData.getData(0) << std::endl;std::cout <<"----int end----" << std::endl;std::cout <<"----char begin----" << std::endl;MyArray<char> charData;for(int i = 1; i <= 10; i++){charData.addData('A'+ i);}charData.print();std::cout <<"--------" << std::endl;charData.deleteData('B');std::cout <<"--------" << std::endl;charData.print();std::cout << charData.getData(1) << std::endl;std::cout <<"----char end----" << std::endl;return 0;
}

七、类模板的特化

类模板的特化是在实例化类模板时,对特定类型的泛型进行特殊的处理,即用户指定所有特定类型的类模板时,通过特化类模板生成的类可能与其他类有完全不同的结构。

特化类模板是需要对整个类模板进行声明定义:

template<>
class 类名<指定类型,指定类型,...>
{
//类成员
};

这里对第六节中的例子添加特化:

#ifndef MYARRAY_H
#define MYARRAY_H//....其他代码不变//对特定泛型的类的成员函数进行特化
template<>
MyArray<char>::MyArray()
{std::cout<< "MyArray<char>::MyArray()" << std::endl;
}//对特定泛型的类进行特化
//这里例子可能不太恰当,只是展示如何对特定类型的类进行特化
template<>
class MyArray<float>
{public:MyArray();private:float data;
};MyArray<float>::MyArray()
{std::cout<< "MyArray<float>::MyArray()" << std::endl;
}#endif // MYARRAY_H

八、类模板的偏特化

偏特化和特化类似,只是特化会指定所有的泛型,而偏特化只指定部分泛型。

偏特化类模板需要对整个类模板进行声明定义:

template <typename T,...不需特化的泛型...>
class 类名<指定类型,...不需特化的泛型名,...>
{//类成员;
}

例子:

#ifndef PAIR_H
#define PAIR_H
#include <iostream>
template<typename T1, typename T2>
class Pair
{
public:Pair();
private:T1 m_first;T2 m_second;
};template<typename T1, typename T2>
Pair<T1, T2>::Pair()
{std::cout << "Pair<T1, T2>::Pair()" << std::endl;
}//偏特化
template<typename T2>
class Pair<char, T2>
{
public:Pair();
private:char first;T2 second;
};template<typename T2>
Pair<char, T2>::Pair()
{std::cout << "Pair<char, T2>::Pair()" << std::endl;
}#endif // PAIR_H
//这是main.cpp
#include <iostream>
#include "Pair.h"
int main()
{Pair<int, int> pNn;Pair<char, int> pCn;return 0;
}

运行结果:

相关文章:

C++ 函数模板和类模板

参考视频&#xff1a;C类模板_哔哩哔哩_bilibili 遗留问题&#xff1a;编译器怎么处理函数模板和类模板 目录 一、为什么会有函数模版&#xff1f;函数模板是为了解决什么问题&#xff1f; 二、函数模板的概念 三、函数模版的使用 四、函数模板的特化 五、类模板的概念 …...

安卓Termux系统设备安装内网穿透工具实现远程使用SFTP传输文件

文章目录 前言1. 安装openSSH2. 安装cpolar3. 远程SFTP连接配置4. 远程SFTP访问4. 配置固定远程连接地址 前言 本教程主要介绍如何在安卓 Termux 系统中使用 SFTP 文件传输&#xff0c;并结合cpolar内网穿透工具生成公网地址&#xff0c;轻松实现无公网IP环境远程传输&#xf…...

文件属性获取

1、getpwuid函数 #include <stdio.h> #include <sys/types.h> #include <pwd.h> int main(int argc, char *argv[]) {uid_t uid 1000;struct passwd * pw getpwuid(uid);printf("name:%s gid:%d info:%s wd:%s shell:%s\n",pw->pw_name,pw-&g…...

C:冒泡排序

1、冒泡排序介绍&#xff1a; 冒泡排序的核心思想就是&#xff1a;两两相邻的元素进行比较。 先用一个例子来帮助大家理解一下冒泡排序的算法是怎们进行的 有一排高矮不同的人站成一列&#xff0c;要按照从矮到高的顺序重新排队。 冒泡排序的方法就是&#xff0c;从第一个人…...

探秘C# LINQ元素运算:原理阐释与实践指南

文章目录 一、LINQ元素运算符概述二. ElementAt 和 ElementAtOrDefault三. First 和 FirstOrDefault四. Last 和 LastOrDefault五. Single 和 SingleOrDefault六. Where 和 Select七、实际应用场景示例总结 LINQ&#xff08;Language-Integrated Query&#xff09;是C#中强大且…...

根据bean的名称获取bean,静态方法查询数据库

根据bean名称获取bean 1.先创建bean&#xff0c;如template package com.test.game.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate;import…...

剪画小程序:音频剪辑新手入门:基础操作指南!

亲爱的小伙伴们&#xff0c;你是否对音频剪辑充满好奇&#xff0c;却不知道从何下手&#xff1f;今天&#xff0c;就让我们用【剪画】一起揭开音频剪辑基础操作的神秘面纱&#xff01; 音频拼接——打造个性音乐串烧 音频拼接是将多个音频片段组合在一起&#xff0c;创造出独特…...

IDEA中maven jar下载失败问题处理

前言 对于在IDEA中maven爆红问题&#xff0c;主要分为两类&#xff0c;一个是全部爆红&#xff0c;一个是部分爆红。 全部爆红 这类问题主要是maven配置没有搞好&#xff0c;可以根据下列步骤自查 1.配置好国内的Maven源 第一步:打开IDEA&#xff0c;查看配置 第二步: 检查…...

C++中,函数返回const类型有什么作用,请举例说明

在C中&#xff0c;函数返回const类型的主要作用是确保返回的对象不会被修改。这种保护机制增强了代码的健壮性和可维护性&#xff0c;尤其是在你希望保证函数返回的数据不被意外篡改时。下面通过几个例子来说明函数返回const类型的作用。 例子 1: 返回常量引用 当你从函数中返…...

Html详解——Vue基础

HTML是什么&#xff1f; 超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;是一种用来结构化 Web 网页及其内容的标记语言。网页内容可以是&#xff1a;一组段落、一个重点信息列表、也可以含有图片和数据表…...

【安规电容知识点总结】

安规电容知识点总结 安规电容简介安规电容的种类X电容和Y电容X电容和Y电容示意图安规电容的型号与应用安规电容简介 安规电容:是指用于这样的场合,即电容器失效后,不会导致电击,不会危及到人身安全。 具体来说,所谓安规电容是一种与普通电容相比符合安全认证的电容,故称…...

R9000P 双系统安装 win11 和 ubuntu

网上了解到一堆关于 r9000p 安装较老的ubuntu系统&#xff0c;会有一堆问题 可能是电脑硬件比较新&#xff0c;较老的系统相关方面不兼容 那么干脆直接装新一点的系统 我安装了 Ubuntu 22.04 1 根据相关教程利用u盘制作系统盘 ultraISO 推荐使用清华源 速度快一点 https://…...

8月8日笔记

8月8日笔记 msf常见命令 启动MSF控制台 msfconsole: 启动MSF控制台。msfconsole -h: 显示帮助菜单。msfconsole -q: 启动MSF控制台并立即退出。 导航和管理 back: 返回上一级菜单。exit: 退出MSF控制台。banner: 显示MSF的横幅。cd: 更改工作目录。color: 开启或关闭彩色输…...

【单片机开发软件】使用VSCode开发STM32环境搭建

&#x1f48c; 所属专栏&#xff1a;【单片机开发软件技巧】 &#x1f600; 作  者&#xff1a; 于晓超 &#x1f680; 个人简介&#xff1a;嵌入式工程师&#xff0c;专注嵌入式领域基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大家&#xff1…...

第十五届蓝桥杯大赛青少组——赛前解析(算法)

算法&#xff1a;进制转换、模拟算法&#xff0c;枚举算法&#xff0c;冒泡排序&#xff0c;插入排序&#xff0c;选择排序&#xff0c;递推算法&#xff0c;递归算法&#xff0c;贪心算法。 1.进制转换 二进制&#xff1a;只包含0和1 八进制&#xff1a;只包含0-7 十进制&…...

工作助手C#研究笔记(5)

通过示例对C#程序的结构逻辑进行研究梳理&#xff0c;虽然通过阅读相关书籍&#xff0c;但是来的效果更慢。一下相关内容可能有误&#xff0c;请谨慎听取。 TaskToDoList-master 1.XAML “XAML”是WPF中专门用于设计UI的语言&#xff0c;优点是 1.XAML可以设计出专业的UI和…...

【kali靶机之serial】--反序列化漏洞实操

kali靶机配置 【我图片里没有截图的默认配置即可】需要改的地方图片里面都有。 使用kali扫描网关的主机。 扫到一个开放了80端口HTTP协议的主机ip 访问80端口 会看到一个文本页面&#xff0c;翻译一下看是什么意思。。 F12查看cookie&#xff0c;是一个base64编码了的东西 使…...

学习大数据DAY34 面向对象思想深化练习 将从豆瓣爬取的数据置入自己搭建的网站上

目录 查看电影类型的电影列表 添加电影 修改电影 上机练习 13 使用三层架构完善 web 系统 查看电影类型的电影列表 DAL.py 文件 class MovieDAL(DBHelper): def getMovieByTid(self,typeid): sqlf"""select id,title,release_date,score,tname from Mo…...

【开端】通过Java 过滤器灵活配置URL访问权限,并返回403

一、绪论 在JAVA项目系统中&#xff0c;后端给前端提供接口。但是在某些场景我们需要临时控制接口是否能被访问。或关闭某一接口的访问权限。 比如某一接口被攻击了或者某一接口存在漏洞&#xff0c;在系统不关闭的情况下&#xff0c;如何控制系统的访问权限。 二、控制接口访…...

【C++综合项目】——基于Boost库的搜索引擎(手把手讲解,小白一看就会!!)

目录 一、前言 二、项目的相关背景 ⚡什么是Boost库&#xff1f;⚡ ⚡什么是搜索引擎&#xff1f;⚡ ⚡为什么要做Boost搜索引擎&#xff1f;⚡ 二、搜索引擎的宏观原理 三、搜索引擎技术栈和项目环境 四、正排索引 VS 倒排索引 —— 搜索引擎的具体原理 &#x…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...