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.mzph.cn/news/42834.shtml

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

相关文章

Axios使用CancelToken取消重复请求

处理重复请求&#xff1a;没有响应完成的请求&#xff0c;再去请求一个相同的请求&#xff0c;会把之前的请求取消掉 新增一个cancelRequest.js文件 import axios from "axios" const cancelTokens {}export const addPending (config) > {const requestKey …

如何区分闰年与平年

首先要明白 地球绕太阳运行周期为365天5小时48分46秒&#xff08;合365.24219天&#xff09;&#xff0c;即一回归年&#xff08;tropical year&#xff09;。公历的平年只有365日&#xff0c;比回归年短约0.2422 日&#xff0c;每四年累积约一天&#xff0c;把这一天加于2月末…

Docker安装基础使用练习

目录 1、安装Docker-CE 1&#xff09;简单使用yum方式安装 ! 2&#xff09;配置镜像加速&#xff1a; 2、下载系统镜像&#xff08;Ubuntu、 centos&#xff09; 1&#xff09;先查看我们所需的镜像有哪些版本。使用search命令&#xff01; 2&#xff09;下载镜像使用的是pul…

【爬虫】P1 对目标网站的背景调研(robot.txt,advanced_search,builtwith,whois)

对目标网站的背景调研 检查 robot.txt估算网站大小识别网站所用技术寻找网站的所有者 检查 robot.txt 目的&#xff1a; 大多数的网站都会包含 robot.txt 文件。该文件用于指出使用爬虫爬取网站时有哪些限制。而我们通过读 robot.txt 文件&#xff0c;亦可以最小化爬虫被封禁的…

vue中实现文字检索时候将搜索内容标红

实现结果 html&#xff1a; <div class"searchBox"><span class"bt">标&#8195&#8195题</span><div class"search"><div class"shuru"><!-- <span class"title">生产经营<…

如何批量修改图片名为不同名称

如何批量修改图片名为不同名称&#xff1f;当今社会&#xff0c;因为人们都养成了随手拍照的习惯&#xff0c;所以拥有上千上万张照片的相册已经司空见惯不足为奇。然而&#xff0c;我们在保存这些照片时往往都会碰到一个大难题——电脑中的图片名称千奇百怪&#xff0c;让整个…

完美解决微信小程序使用复选框van-checkbox无法选中

由于小程序使用了vant-ui框架&#xff0c;导致checkbox点击无法选中问题 <van-checkbox value"{{ checked }}" shape"square"><view class"check-content"><view class"checktext">我已阅读并同意>《用户协议》…

opencv-目标追踪

import argparse import time import cv2 import numpy as np# 配置参数 ap argparse.ArgumentParser() ap.add_argument("-v", "--video", typestr,help"path to input video file") ap.add_argument("-t", "--tracker", …

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

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

java Spring Boot properties多环境配置拆分文件管理

上文 java Spring Boot yml多环境拆分文件管理优化 我们用yml 做了一个多环境配置文件的拆分管理 我们将 application.yml 改为 application.properties 参考代码如下 spring.profiles.activedev我们知道 yml 是用 : 来区分高低基本 而 properties是直接通过 . 来表达 其他基本…

SpringCloud实用篇7——深入elasticsearch

目录 1 数据聚合1.1 聚合的种类1.2 DSL实现聚合1.2.1 Bucket聚合语法1.2.2 聚合结果排序1.2.3 限定聚合范围1.2.4 Metric聚合语法1.2.5.小结 1.3 RestAPI实现聚合1.3.1 API语法1.3.2 业务需求1.3.3 业务实现 2 自动补全2.1 拼音分词器2.2 自定义分词器2.3 自动补全查询2.4 实现…

POJ 1995 Raising Modulo Numbers 快速幂

一、总结 我一开始担心溢出&#xff0c;开了一个无符号的long long&#xff0c;但是直接超时&#xff0c;后来一看它的mod不是很大&#xff0c;于是改成int&#xff0c;直接过了。 二、代码 #include <iostream> using namespace std; int H, Z; int M; int mulMod(in…

Python基础教程:私有变量的访问和赋值教程

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 首先我们这里先描述下&#xff1a; Python中&#xff0c;变量名类似__x__的&#xff0c;以双下划线开头&#xff0c;并且以双下划线结尾的&#xff0c;是特殊变量&#xff0c;特殊变量是可以直接访问的&#xff08;比如 doc, __i…

SpringBoot3集成ElasticSearch

标签&#xff1a;ElasticSearch8.Kibana8&#xff1b; 一、简介 Elasticsearch是一个分布式、RESTful风格的搜索和数据分析引擎&#xff0c;适用于各种数据类型&#xff0c;数字、文本、地理位置、结构化数据、非结构化数据&#xff1b; 在实际的工作中&#xff0c;历经过Ela…

QT的设计器介绍

设计器介绍 Qt制作 UI 界面&#xff0c;一般可以通过UI制作工具QtDesigner和纯代码编写两种方式来实现。纯代码实现暂时在这里不阐述了在后续布局章节详细说明&#xff0c;QtDesigner已经继承到开发环境中&#xff0c;在工程中直接双击ui文件就可以直接在QtDesigner设计器中打…

【100天精通python】Day39:GUI界面编程_PyQt 从入门到实战(下)_图形绘制和动画效果,数据可视化,刷新交互

目录 专栏导读 6 图形绘制与动画效果 6.1 绘制基本图形、文本和图片 6.2 实现动画效果和过渡效果 7 数据可视化 7.1 使用 Matplotlib绘制图表 7.2 使用PyQtGraph绘制图表 7.3 数据的实时刷新和交互操作 7.3.1 数据的实时刷新 7.3.2 交互操作 7.4 自定义数据可视化…

【WPF】 本地化的最佳做法

【WPF】 本地化的最佳做法 资源文件英文资源文件 en-US.xaml中文资源文件 zh-CN.xaml 资源使用App.xaml主界面布局cs代码 App.config辅助类语言切换操作类资源 binding 解析类 实现效果 应用程序本地化有很多种方式&#xff0c;选择合适的才是最好的。这里只讨论一种方式&#…

Unity制作一个简单的登入注册页面

1.创建Canvas组件 首先我们创建一个Canvas画布&#xff0c;我们再在Canvas画布底下创建一个空物体&#xff0c;取名为Resgister。把空物体的锚点设置为全屏撑开。 2.我们在Resgister空物体底下创建一个Image组件&#xff0c;改名为bg。我们也把它 的锚点设置为全屏撑开状态。接…

【深入理解ES6】字符串和正则表达式

概念 字符串&#xff08;String&#xff09;是JavaScript6大原始数据类型。其他几个分别是Boolean、Null、Undefined、Number、Symbol&#xff08;es6新增&#xff09;。 更好的Unicode支持 1. UTF-16码位 字符串里的字符有两种&#xff1a; 前 个码位均以16位的编码单元…