C++函数模板和类模板

C++另一种编程思想称为泛型编程,主要利用的技术是模板
C++提供两种模板机制:函数模板和类模板

C++提供了模板(template)编程的概念。所谓模板,实际上是建立一个通用函数或类,
其类内部的类型和函数的形参类型不具体指定, 用一个虚拟的类型来代表。
这种通用的方式称为模板。模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

即:我们提供一个抽象的函数或类,并不具体指定其中数据的类型,而是某个虚拟类型代替。只提供基本的功能。其具体的数据类型,只在其被调用时视具体情况实例化。

函数模板

举个例子

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函数声明与定义
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(10, 5)=" << test(10, 5) << endl;     //调用模板函数,模板函数通过传入的参数自动推导未实例化的类型cout << "test(5,'A')=" << test(5,'A') << endl;cout << "test(10.5, 5.5) =" << test(10.5, 5.5) << endl;system("pause");return 0;
}

在这里插入图片描述
函数模板的声明通过关键字template与typename 实现。其中,template告知编译器这是函数模板的声明,typename用来声明虚拟类型。比如你要声明一个模板函数,里面需要两个不同的变量,那么你就需要通过typename声明两个不同的虚拟类型T1,T2。

声明好后,你就可以在函数定义中使用虚拟类型来定义变量,但是要注意,用同一个虚拟类型定义的变量就只能是同种类型,比如用T1定义的变量只能是同种变量,可以是int,也可以是char。这取决于其实例化时被实例化为哪种类型。

C++函数模板注意事项
注意事项:
1、自动类型推导,必须推导出一致的数据类型T,才可以使用
2、模板必须要确定出T的数据类型,才可以使用

using namespace std;
template<class T>
void mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void test01()
{int a = 10;int b = 20;char c = 'c';//1、自动类型推导,必须推导出一致的数据类型T,才可以使用mySwap(a, b);//mySwap(a, c);推导不出一致的T类型cout << "a = " << a << endl;cout << "b = " << b << endl;
}template<class T>
void func()
{cout << "func()的调用" << endl;
}void test02()
{//2、模板必须要确定出T的数据类型,才可以使用func<int>();
}int main() {test01();test02();return 0;
}

模板函数的调用
1)显式类型调用
可以显式的调用模板函数,即在调用时人为地指明虚拟类型的具体类型。

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函数声明与定义
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(5,'A')=" << test<int,char>(5, 'A') << endl;          //<int,char>显式的指明模板的类型system("pause");return 0;
}

2)自动推导
即不指明具体的数据类型,而让编译器根据传入的数据自动推导出数据类型。

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函数声明与定义
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(5,'A')=" << test(5, 'A') << endl;          //自动推导数据类型system("pause");return 0;
}

模板函数与函数重载

熟悉函数重载的人应该会好奇,如果既有模板函数又有同名的普通函数,而且参数列表的参数个数是一样的,那么在主函数中调用同名函数,编译器具体会调用哪一个呢?

下面看一个例子:

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函数声明与定义
T1 test(T1 tmp, T2 tmp1) {cout << "调用模板函数!" << endl;return (tmp + tmp1);
}int test(int tmp, int tmp1) {                  //重载的普通函数cout << "调用普通函数!" << endl;return 0;
}int main(void) {char tmp = 'c';int tmp1 = 0;int a = 5;cout << "test(5,'c')=" << test(a, tmp) << endl;     cout << "test(5,0)=" << test(a, tmp1) << endl;system("pause");return 0;
}

在这里插入图片描述
普通函数的两个参数都是int型,在第一次调用test时第二个参数使用的是char型,调用的是模板函数,第二次使用的是int型,调用的是普通函数。

这是为什么呢?理论上来说,模板函数两个都能匹配,使用。而普通函数也能匹配这两次调用的参数(在C语言中,char型变量是可以作为int型参数使用的)。

这是因为模板函数可以自动推导类型,在第一次调用中,两个类型分别被推导为int型与char型。而普通函数是两个int型,虽然也能使用传入的参数,但模板函数明显能更好的匹配参数列表。

也就是说,如果模板函数实例化后的类型能更好的匹配参数列表的话就使用模板函数。

那么当这两个函数都能完全匹配参数列表的时候呢?通过第二次test的调用结果不难发现,这时候,编译器会调用普通函数。

如果一定要使用模板函数,可以使用<>显式的指定使用模板函数。看下面的例子。

#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2>             //模板函数声明与定义
T1 test(T1 tmp, T2 tmp1) {cout << "调用模板函数!" << endl;return (tmp + tmp1);
}int test(int tmp, int tmp1) {                  //重载的普通函数cout << "调用普通函数!" << endl;return 0;
}int main(void) {char tmp = 'c';int tmp1 = 0;int a = 5;cout << "test(5,'A')=" << test(a, tmp) << endl;     cout << "test<>(5,0)=" << test<>(a, tmp1) << endl;       //使用<>显式的调用模板函数system("pause");return 0;
}

在这里插入图片描述

类模板

类模板是为了减少重复工作量而出现的一种进制,当一个类的功能类似只是类型不同时,一个通用的类模板可以根据使用者的需要而生成具体类型的类,从而减少功能重复的代码。

类模板作用:建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表
解释:
template–声明创建模板
typename–表明其后面的符号是一种数据类型,可以用class代替
T–通用的数据类型,名称可以替换,通常为大写字母

在类内部定义与声明

#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class DEMO {public:DEMO(T data) {this->data = data;}~DEMO() {}int GetData() {return this->data;}private:T data;
};template <typename T,typename T1>
class DEMO1 {public:DEMO1() {}~DEMO1();private:T data;T1 ch;
};int main(void) {DEMO<int> demo(5);           //显示的指定类型为intDEMO1<int, char> demo1();    //显示的指定类型分别为int,charcout << "data=" << demo.GetData() << endl;system("pause");return 0;
}

类模板的定义与使用使用的是和模板函数一样的关键字。即先声明template,在使用typename声明虚拟类型。

与模板函数不同的是,类模板不能被自动推导出类型,只能显示的指定具体的类型。如上面代码中的 DEMO< int > demo(5),该模板类被显示的指定为int型。

在类外部定义成员函数

在类内部声明成员函数,在类外部定义成员函数时,只要成员函数参数列表中出现了类限定域说明,模板类作为返回类型,模板类作为参数类型,那么就要在成员函数之前声明 template <类型形式参数表>,并且在模板类后加上虚拟参数列表。

#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class DEMO {public:DEMO(T data);~DEMO();DEMO operator+(int sum);int PrintData(DEMO& demo);private:T data;
};template<typename T>          //出现了类限定域说明
DEMO<T>::DEMO(T data)
{this->data = data;
}template<typename T>         //出现了类限定域说明
DEMO<T>::~DEMO()
{
}template<typename T>                //出现了作为返回值类型的模板类类型
DEMO<T> DEMO<T>::operator+(int sum)
{return *this;
}template<typename T>               //出现了作为参数类型的模板类类型
int DEMO<T>::PrintData(DEMO<T>& demo)
{cout << "data=" << demo.data << endl;return 0;
}int main(void) {DEMO<int> demo(5), demo1(15);demo.PrintData(demo1);demo + 5;system("pause");return 0;
}

在这里插入图片描述
DEMO< T >中的< T >是虚拟参数列表。

总结来说,只要看到了模板类的类名关键字出现在成员函数参数列表中,就要在成员函数之前声明 template <类型形式参数表>,并且在模板类类名后加上虚拟参数列表。

模板类的继承

分为三种情况。一:子类是模板类,父类是普通类;二:父类是模板类,子类是普通类;三:父类与子类都是模板类。其中第一种情况与两个普通类的继承是一样的。就不说了。

1)父类是模板类,子类是普通类:

#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class FATHER {public:FATHER(T data) {this->data = data;}~FATHER() {}private:T data;
};class SON:public FATHER<int> {        //显示的指明父类的具体类型public:SON(int data):FATHER<int>(data) {this->data = data;}~SON() {}int GetData() {return data;}private:int data;
};int main(void) {SON son(15);cout << "data=" << son.GetData() << endl;system("pause");return 0;
}

子类是一般类,父类是模板类,继承时必须在子类里实例化父类的类型参数。其实这很好理解,因为在子类对象构造之前,会先调用父类的构造函数,父类为模板类,要想实例化,需要有指定的类型。

2)父类与子类都是模板类:

#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class FATHER {public:FATHER(T data) {this->data = data;}~FATHER() {}private:T data;
};template <typename T1>
class SON:public FATHER<T1> {       //使用子类的模板类型传递到父类中,也可以使用具体的类型public:SON(int data):FATHER<int>(data) {this->data = data;}~SON() {}int GetData() {return data;}private:T1 data;
};int main(void) {SON<int> son(15);cout << "data=" << son.GetData() << endl;system("pause");return 0;
}

当子类与父类都是模板类时,继承时也必须在子类里实例化父类的类型参数,值得注意的是,此时实例化的类型参数可以使用子类的模板类型。即让父类与子类在实例化后拥有一样的具体类型。当然也可以使用其它的具体类型。

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

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

相关文章

算法与数据结构(七)--堆

一.堆 1.堆的定义 堆是计算机科学中一类特殊的数据结构的通常&#xff0c;堆通常可以被看做是一颗完全二叉树的数组对象。 堆的特性 1.它是完全二叉树&#xff0c;除了树的最后一层结点不需要是满的&#xff0c;其他的每一层从左到右都是满的&#xff0c;如果最后一层结点不…

WordPress更换域名后-后台无法进入,网站模版错乱,css失效,网页中图片不显示。完整解决方案(含宝塔设置)

我在实际解决问题时用到了 【简单暴力解决方案】的《方法一&#xff1a;修改wp-config.php》 和 【简单暴力-且特别粗暴-的解决方案】 更换域名时经常遇到的几个问题&#xff1a; 1、更换域名后&#xff0c;后台无法进入 2、更换域名后&#xff0c;网站模版错乱&#xff0c;c…

基于物理场的动态模式分解(piDMD)研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

stm32红绿灯源代码示例(附带Proteus电路图)

本代码不能直接用于红路灯&#xff0c;只是提供一个思路 #include "main.h" #include "gpio.h" void SystemClock_Config(void); void MX_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStruct {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOB_CLK_ENAB…

九耶丨阁瑞钛伦特-在项目中找到的经典BUG是什么?

在项目中找到的经典BUG有很多种&#xff0c;以下是其中一些常见的例子&#xff1a; 空指针异常&#xff08;NullPointerException&#xff09;&#xff1a;当程序试图访问一个空对象或未初始化的变量时&#xff0c;会抛出空指针异常。这通常是由于缺少对变量的正确初始化或检查…

RabbitMq-发布确认高级(避坑指南版)

在初学rabbitMq的时候&#xff0c;伙伴们肯定已经接触到了“发布确认”的概念&#xff0c;但是到了后期学习中&#xff0c;会接触到“springboot”中使用“发布确认”高级的概念。后者主要是解决什么问题呢&#xff1f;或者是什么样的场景引出这样的概念呢&#xff1f; 在生产环…

第1天----验证一个字符串是否是另一个字符串的子串

本文我们将学习如何去验证一个字符串是否是另一个字符串的子串。 一、小试牛刀&#xff1a; 题目描述 输入两个字符串&#xff0c;验证其中一个串是否为另一个串的子串。 输入格式 两行&#xff0c;每行一个字符串。 输出格式 若第一个串 s 1 是第二个串 s 2 的子串&#xff0c…

[Machine Learning] decision tree 决策树

&#xff08;为了节约时间&#xff0c;后面关于机器学习和有关内容哦就是用中文进行书写了&#xff0c;如果有需要的话&#xff0c;我在目前手头项目交工以后&#xff0c;用英文重写一遍&#xff09; &#xff08;祝&#xff0c;本文同时用于比赛学习笔记和机器学习基础课程&a…

【java安全】Log4j反序列化漏洞

文章目录 【java安全】Log4j反序列化漏洞关于Apache Log4j漏洞成因CVE-2017-5645漏洞版本复现环境漏洞复现漏洞分析 CVE-2019-17571漏洞版本漏洞复现漏洞分析 参考 【java安全】Log4j反序列化漏洞 关于Apache Log4j Log4j是Apache的开源项目&#xff0c;可以实现对System.out…

SQL Server2019安装后使用SQL Server身份验证登录失败

错误情况 今天在电脑安装SQL Server2019和SMMS&#xff0c;安装过程一切顺利&#xff0c;但是在使用SMMS连接数据库时出现了异常。使用"Window 身份验证"登录时正常&#xff0c;但是如果改为使用"SQL Server 身份验证"登录时却连接失败&#xff01; 解决方…

VS2022远程Linux使用cmake开发c++工程配置方法

文章目录 远程连接CMakePresets.json的配置Task.vs.json配置launch.vs.json配置最近使用别人在VS2015上使用visualgdb搭建的linux开发环境,各种不顺手,一会代码不能调转了,一会行号没了,调试的时候断不到正确的位置,取消的断点仍然会进。因此重新摸索了一套使用vs的远程开…

js判断用户当前网络状态和判断网速

前端判断用户当前网络状态和判断网速 一、第一种是通过 HTML5 提供的 navigator 去检测网络(1)、原理介绍:(2)、兼容性 二、监听window.ononline和window.onoffline事件:三、通过ajax进行请求判断(兼容性好-推荐)(1)、原理介绍:(2)、注意: 四、navigator.connection方法监听网络…

13---内嵌HTML和React

虽然Markdown本身不支持内嵌HTML和React&#xff0c;但可以在Markdown文档中直接插入HTML代码和React组件。 一、在markdown中内嵌HTML 在Markdown中&#xff0c;你可以使用HTML标签来实现更复杂的样式和布局。例如&#xff0c;你可以使用<div>标签来创建一个容器&#…

postgresql 分类排名

postgresql 分类排名 排名窗口函数示例CUME_DIST 和 NTILE 排名窗口函数 排名窗口函数用于对数据进行分组排名。常见的排名窗口函数包括&#xff1a; • ROW_NUMBER&#xff0c;为分区中的每行数据分配一个序列号&#xff0c;序列号从 1 开始分配。 • RANK&#xff0c;计算每…

私密数据采集:隧道爬虫IP技术的保密性能力探究

作为一名专业的爬虫程序员&#xff0c;今天要和大家分享一个关键的技术&#xff0c;它能够为私密数据采集提供保密性能力——隧道爬虫IP技术。如果你在进行敏感数据采集任务时需要保护数据的私密性&#xff0c;那么这项技术将是你的守护神。 在进行私密数据采集任务时&#xff…

前端性能优化——包体积压缩插件,打包速度提升插件,提升浏览器响应的速率模式

前端代码优化 –其他的优化可以具体在网上搜索 压缩项目打包后的体积大小、提升打包速度&#xff0c;是前端性能优化中非常重要的环节&#xff0c;结合工作中的实践总结&#xff0c;梳理出一些 常规且有效 的性能优化建议 ue 项目可以通过添加–report命令&#xff1a; "…

nginx上web服务的基本安全优化、服务性能优化、访问日志优化、目录资源优化和防盗链配置简介

一.基本安全优化 1.隐藏nginx软件版本信息 2.更改源码来隐藏软件名和版本 &#xff08;1&#xff09;修改第一个文件&#xff08;核心头文件&#xff09;&#xff0c;在nginx安装目录下找到这个文件并修改 &#xff08;2&#xff09;第二个文件 &#xff08;3&#xff09;…

Selenium 自动化 | 案例实战篇

Chrome DevTools 简介 Chrome DevTools 是一组直接内置在基于 Chromium 的浏览器&#xff08;如 Chrome、Opera 和 Microsoft Edge&#xff09;中的工具&#xff0c;用于帮助开发人员调试和研究网站。 借助 Chrome DevTools&#xff0c;开发人员可以更深入地访问网站&#xf…

C++11并发与多线程笔记(9) async、future、packaged_task、promise

C11并发与多线程笔记&#xff08;9&#xff09; async、future、packaged_task、promise 1、std::async、std::future创建后台任务并返回值2、std::packaged_task&#xff1a;打包任务&#xff0c;把任务包装起来3、std::promise3、小结 1、std::async、std::future创建后台任务…

Amazon CloudFront 部署小指南(六)- Lambda@Edge 基础与诊断

内容简介 本文适用于希望使用 Amazon CloudFront LambdaEdge 提升 Amazon CloudFront 边缘计算能力的用户&#xff0c;旨在帮助您更好的进行 CloudFront LambdaEdge 的开发、调试、测试、部署等工作。 首先我们会对 CloudFront LambdaEdge 做个简单的介绍&#xff0c;然后分七个…