C++ 函数模板与类模板

C++最重要的特性之一就是代码重用,为了实现代码重用,代码必须具有通用性。通用代码应不受数据类型的影响,并且可以自动适应数据类型的变化。这种程序设计类型称为参数化程序设计。模板是C++支持参数化程序设计的工具,通过它可以实现参数化多态性。所谓参数化多态性,就是将程序所处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。

1.函数模板

通过函数重载,可以看出重载函数通常是对于不同的数据类型完成类似的操作。很多情况下,一个算法是可以处理多种数据类型的。但是用函数实现算法时,即使设计为重载函数也只是使用相同的函数名,函数体仍然要分别定义。

下面是两个求绝对值的函数:

int abs(int x)
{return x < 0 ? -x : x;
}double abs(double x)
{return x < 0 ? -x : x;
}

这两个函数只有参数类型和返回类型不同,功能完全一样。类似这样的情况,我们需要写一段通用的代码是用于多种不同的数据类型,这样会使代码的可重用性大大提高,从而提高软件的开发效率。使用函数模板就是为了达到这一目的。程序员只对函数模板编写一次,然后基于调用函数时提供的参数类型,C++编译器将自动产生相应的函数来正确地处理该类型的数据。

(1)函数模板的定义形式为
template <模板参数表>
类型名 函数名(参数表)
{函数体定义
}

所有函数模板的定义都是用关键字template开始的,该关键字之后是用尖括号<>括起来的“模板参数表”。模板参数表由用逗号隔开的模板参数构成,可以包括以下内容:

①class(或typedef)标识符,指明可以接收一个类型参数。这些类型参数代表的是类型,可以是内部类型或者自定义类型。
②“类型说明符”标识符,指明可以接收一个由“类型说明符”所规定类型的常量作为参数。
③template<参数表>class标识符,指明可以接收一个类模板名作为参数。

类型参数可以用来指定函数模板本身的形参类型、返回值类型,以及声明函数中的局部变量。函数模板中函数体的定义方式与定义普通函数类似。

【例1】求绝对值的函数模板

template<class T>
T abs(T x)
{return x < 0 ? -x : x;
}
int main()
{int n = -5;cout << abs(n) << endl;double m = -6.8;cout << abs(m) << endl;return 0;
}

运行结果:
在这里插入图片描述
分析:

①在上述主函数中调用abs()时,编译器从实参的类型推导出函数模板的类型参数。

②当类型参数的含义确定后,编译器将以函数模板为样板,生成一个函数,这一过程称为函数模板的实例化。

例如,对于调用表达式abs(n),由于实参n是int类型,所以推导出函数模板中类型参数T为int,接着,编译器以函数模板为样板,生成如下函数,该函数为函数模板abs的一个实例:

int abs(int x)
{return x < 0 ? -x : x;
}

同样,对于调用表达式abs(m),由于实参m是double型,所以推导出函数模板中类型参数T为double,接着,编译器以函数模板为样板,生成如下函数:

double abs(double x)
{return x < 0 ? -x : x;
}

③因此,当主函数第一次调用abs时,执行的实际上是由函数模板生成的函数int abs(int x);,主函数第二次调用abs时,执行的实际上是由函数模板生成的函数double abs(double x);

【例2】函数模板示例

template<class T>
void outputA(const T* arr, int n)
{for (int i = 0;i < n; i++){cout << arr[i] << " ";}cout << endl;
}int main()
{const int A_n = 5;const int B_n = 6;const int C_n = 7;int arr[A_n] = { 1,2,3,4,5 };cout << "输出数组arr的内容:" << "   ";outputA(arr, A_n);double brr[B_n] = { 1.1,2.2,3.3,4.4,5.5,6.6 };cout << "输出数组brr的内容:" << "   ";outputA(brr, B_n);char crr[C_n] = "Hi yyn";cout << "输出数组crr的内容:" << "   ";outputA(crr, C_n);return 0;
}

运行结果:
在这里插入图片描述
分析:

函数模板中声明了类型参数T,表示一种抽象的类型。当编译器检测到程序中调用函数模板outputA时,便用outputA的第一个实参的类型替换掉整个模板定义中的T,并建立用来输出指定类型数组的一个完整的函数,然后再编译这个新建的函数。

主函数中声明了3中不同类型的数组,int型数组arr,double型数组brr和char型数组crr,长度分别为5,6,7。然后调用函数模板生成相应的函数,最后在屏幕上输出每个数组。编译过程中针对3种数据类型生成的函数如下:

outputA(a,A_n);//适用于int类型的outputA模板函数
outputA(b,B_n);//适用于double类型的outputA模板函数
outputA(c,C_n);//适用于char类型的outputA模板函数

由上例可以看出,模板函数与重载密切相关。从函数模板产生的相关函数都是同名的,编译器用重载的方法调用相应的函数。另外函数模板本身也可以用多种方法重载。

(2)模板函数的使用形式和函数的本质区别

①函数模板本身在编译时不会生成任何目标代码,只有由模板生成的实例会生成目标代码。

②被多个源文件引用的函数模板,应当连同函数体一同放在头文件中,而不能像普通函数那样只将声明放在头文件中。

③函数指针也只能指向函数模板的实例,而不能指向函数模板本身。

2.类模板

使用类模板使用户可以为类定义一种模式,使得类中的某些数据成员、某些成员函数的参数、返回值或局部变量能取任意类型(包括系统预定义的和用户自定义的)。

类是对一组对象的公共性质的抽象,而类模板则是对不同类的公共性质的抽象,因此,类模板是属于更高层次的抽象。由于类模板需要一种或多种类型参数,所以类模板也常常称为参数化类。

vector就是一个类模板,用vector创建的动态数组都是类模板的对象。

(1)类模板声明的语法形式
template<模板参数表>
class 类名
{类成员声明;
};

其中类成员的声明方法和普通类的定义几乎相同,只是它的各个成员(数据成员和函数成员)中通常要用到模板的类型参数T。其中“模板参数表”的形式与函数模板中的“模板参数表”相同。

如果需要在类模板以外定义其成员函数,则要采用以下的形式:

template<模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数表)

一个类模板声明,其自身并不是一个类,它说明了类的一个家族,只有被其他代码引用时,类模板才根据引用的需要生成具体的类。类模板的实例化过程在程序中时隐藏的。

使用一个类模板建立对象时,应以如下形式声明:

模板名<模板参数表>对象名1,...,对象名n;

【例】类模板应用举例
在本例中,声明一个实现任意类型数据存取的类模板S,然后通过具体数据类型参数对类模板进行实例化,生成类,然后类在被实例化生成对象s1,s2,s3和d。

struct student//结构体student
{int id;//学号float avg;//平均分
};template<class T>//类模板:实现对任意类型数据进行存取
class S
{
private:T item;//用于存放任意类型的数据bool Isvalue;//标记item是否被存入
public:S();//默认构造函数T& getE();//提取数据函数void putE(const T& x);//存入数据函数
};template<class T>//默认构造函数的实现
S<T>::S():Isvalue(false){}template<class T>//提取数据函数的实现
T&S<T>::getE()
{	if (!Isvalue)//如果提取的是没有初始化的数据,则程序终止{cout << "数据不存在" << endl;exit(1);//使程序完全退出,返回到操作系统//参数可用来表示程序终止的原因,可以被操作系统接收}elsereturn item;//返回item中存放的数据
}template<class T>//存入函数的实现
void S<T>::putE(const T& x)
{Isvalue = true;//将Isvalue设置为true,表示item中已存入数值item = x;//将x的值存入item
}int main()
{S<int>s1, s2;//定义两个S<int>类对象s1和s2,其中数据成员item为int型s1.putE(3);//向对象s1中存入数据(初始化对象s1为3)s2.putE(-7);//向对象s2中存入数据(初始化对象s1为-7)cout << s1.getE() << " " << s2.getE() << endl;//输出对象s1和s2的数据成员student g = { 1000,23 };//定义student类型结构体变量的同时赋予初值S<student>s3;//定义S<student>类对象s3,其中数据成员item为student类型s3.putE(g);//向对象s3中存入数据(初始化对象s3)cout << "这个学生的id是" << s3.getE().id << endl;//输出对象s3的数据成员S<double>d;//定义S<double>类对象d,其中数据成员item为double类型cout << "检索对象d";cout << d.getE() << endl;//输出对象d的数据成员//由于对象d未经初始化,在执行函数d.getE()过程中导致程序终止return 0;
}

运行结果:
在这里插入图片描述

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

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

相关文章

前端进阶js02----null和undefined的区别

1.相同点 1)都是原始类型的值且保存在栈中。 2) 在布尔运算中都会被认为是false 2.不同点 1&#xff09;null是js的关键字&#xff0c;表示空值&#xff1b;undefined不是关键字&#xff0c;是一个全局变量。 2&#xff09;值相同&#xff0c;但类型不一样 值相同&#xff1a…

考研算法第40天:众数 【模拟,简单题】

题目 本题收获 又是一道比较简单的模拟题&#xff0c;就不说解题思路了&#xff0c;说一下中间遇到的问题吧&#xff0c;就是说cin输入它是碰到空格就停止输入的&#xff0c;详细的看下面这篇博客对于cin提取输入流遇到空格的问题_while(cin) 空格_就是那个党伟的博客-CSDN博…

JavaScript:模块化【导入和导出】

在现代软件开发中&#xff0c;代码的组织和管理是至关重要的&#xff0c;尤其是在大型项目中。模块化是一种解决方案&#xff0c;它允许我们将代码分割成独立的部分&#xff0c;使得代码结构更清晰、易于维护和重用。本文将深入探讨模块的导入和导出&#xff0c;帮助你全面理解…

为什么都劝年轻人不要频繁跳槽?

"为什么都劝年轻人不要频繁跳槽&#xff1f;"这句话绝对正确&#xff0c;没有任何漏洞&#xff0c;无论如何解释都是正确的&#xff0c;因为“频繁”这个词是非常主观的&#xff0c;有很大的弹性。 不同的人对于跳多少次才算频繁有不同的看法&#xff0c;有人认为一…

QT-QlineEdit 添加自动补全

要在 QLineEdit 上添加自动补全功能&#xff0c;你可以使用 QCompleter 类。QCompleter 提供了自动补全的功能&#xff0c;并且可以与 QLineEdit 一起使用。 下面是一个示例代码&#xff0c;演示了如何在 QLineEdit 上添加自动补全&#xff1a; #include <QApplication>…

大数据培训课程-《机器学习从入门到精通》上新啦

《机器学习从入门到精通》课程是一门专业课程&#xff0c;面向人工智能技术服务&#xff0c;课程系统地介绍了Python编程库、分类、回归、无监督学习和模型使用技巧以及算法和案例充分融合。 《机器学习从入门到精通》课程亮点&#xff1a; 课程以任务为导向&#xff0c;逐步学…

nux查看线程数量及优先级

linux查看线程数量及优先级 在Linux中&#xff0c;可以使用以下命令查看线程名称和PID&#xff1a; top -H ps -T这些命令将会列出当前系统中正在运行的所有进程及其创建的线程。 如果要查看线程的优先级&#xff0c;可以通过如下命令&#xff1a; /root # chrt -p 1091 pi…

Python自动化测试用例:如何优雅的完成Json格式数据断言

目录 前言 直接使用 优化 封装 小结 进阶 总结 资料获取方法 前言 记录Json断言在工作中的应用进阶。 直接使用 很早以前写过一篇博客&#xff0c;记录当时获取一个多级json中指定key的数据&#xff1a; #! /usr/bin/python # coding:utf-8 """ aut…

Shell编程基础(十一)使用 expect 脚本处理人机交互

使用 expect 脚本处理人机交互 安装expect适用场景编写expect实现人机交互自动确认删除文件登录远程服务器自动确认并输入密码 在shell中执行 expect 脚本程序 安装expect 先检测是否有安装 yum list installex expect或者 使用 rpm rpm -q expect如果没有安装&#xff0c;就…

访问者模式(Visitor)

访问者模式是一种行为设计模式&#xff0c;可封装一些作用于当前数据结构的各元素的操作&#xff0c;它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 Visitor is a behavior design pattern that encapsulates some operations that act on the elements of t…

在前端中如何创建表单并提交数据到服务器?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 通过from表单⭐ 代码讲解⭐ 注意事项⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发…

积累常见的有针对性的python面试题---python面试题001

1.考点列表的.remove方法的参数是传入的对应的元素的值,而不是下标 然后再看remove这里,注意这个是,删除写的那个值,比如这里写3,就是删除3, 而不是下标. remove不是下标删除,而是内容删除. 2.元组操作,元组不支持修改,某个下标的内容 可以问他如何修改元组的某个元素 3.…

Vue3 第五节 一些组合式API和其他改变

1.provide和inject 2.响应式数据判断 3.Composition API的优势 4.新的组件 5.其他改变 一.provide和inject 作用&#xff1a;实现祖与后代组件间通信 套路&#xff1a;父组件有一个provide选项来提供数据&#xff0c;后代组件有一个inject选项来开始使用这些数据 &…

PHP生成随机数

要在 PHP 中生成随机密码&#xff0c;可以使用 rand() 函数和字符串操作函数。以下是一个示例代码来生成包含字母、数字和特殊字符的随机密码 function generateRandomPassword($length 8) {// 定义包含字母、数字和特殊字符的字符集$characters abcdefghijklmnopqrstuvwxyz…

概念解析 | AutoFed:面向异构数据的联邦多模态自动驾驶的学习框架

AutoFed:面向异构数据的联邦多模态自动驾驶的学习框架 注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:面向异构数据的联邦学习在自动驾驶中的应用。 参考文献:Zheng T, Li A, Chen Z, et al. AutoFed: Heterogeneity-Aware F…

AcWing算法提高课-1.3.17背包问题求具体方案

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 本题链接&#xff08;AcWing&#xff09; 点这里 题目描述 有 N N N 件物品和一个容量是 V V V 的背包。每件物品只能使用一次。 第 i i i 件物品的体积是 v i v_i vi​&#xff0c;价值…

HEIF—— 1、vs2017编译Nokia - heif源码

HEIF(高效图像文件格式) 一种图片有损压缩格式,它的后缀名通常为".heic"或".heif"。 HEIF 是由运动图像专家组 (MPEG) 标准化的视觉媒体容器格式,用于存储和共享图像和图像序列。它基于著名的 ISO 基本媒体文件格式 (ISOBMFF) 标准。HEIF读写器引擎…

【C++】位图|布隆过滤器|海量数据处理面试题

文章目录 一.位图1. 位图的概念2. 位图的使用3. 位图的实现 二.布隆过滤器1. 布隆过滤器2. 布隆过滤器的实现 三.海量数据处理面试题1.位图2.布隆过滤器3.哈希切割 一.位图 1. 位图的概念 所谓位图&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于海量数据&#xf…

android模拟器创建失败,出现“The emulator process for AVD xxx has terminated.”的错误

查找问题&#xff1a; 参考文章&#xff1a; 解决 Android studio 出现“The emulator process for AVD xxx has terminated.”的错误_the emulator process for avd pixel_3a_api_34_exten_千里未归jan的博客-CSDN博客

【LeetCode】删除并获得点数

删除并获得点数 题目描述算法分析编程代码空间优化 链接: 删除并获得点数 题目描述 算法分析 编程代码 class Solution { public:int deleteAndEarn(vector<int>& nums) {const int N 10001;int arr[N] {0};for(const auto& n : nums){arr[n]n;}vector<in…