【C++】模板入门教程

C++模板是C++编程中的重要利器,能够大大提高编码效率。函数模板和类模板是模板编程中的两个重要概念,它们允许我们编写独立于数据类型的通用代码

本文将深入介绍函数模板和类模板的使用方法,以及它们的实现原理、匹配规则和注意事项,帮助读者更好地掌握C++模板知识,从而在实际编程中提高效率。

目录

  • 1. 概述
    • 1.1 泛型编程
    • 1.2 模板的引入
  • 2. 函数模板
    • 2.1 定义和使用方法
    • 2.2 原理
    • 2.3 实例化
      • 2.3.1 隐式实例化
      • 2.3.2 显式实例化
    • 2.4 匹配规则
  • 3. 类模板
    • 3.1 定义类模板
    • 3.2 类模板的实例化
  • 4. 结论

1. 概述

1.1 泛型编程

泛型编程的核心思想是编写独立于特定数据类型的代码。这意味着程序员可以定义一套算法或数据结构,而不必为每种可能的数据类型编写特定的代码实现。这种思想的实现极大地提高了代码的复用性和程序的可维护性

想象一下,如果没有泛型编程,对于简单的数据结构如列表、栈或队列,我们可能需要为整型、浮点型、字符串等每一种数据类型编写特定的实现。这不仅会导致大量重复的代码,也会使得维护和更新变得异常困难。

泛型编程的一个经典例子是排序算法。不论是整数数组还是浮点数数组,甚至是自定义类型的数组,排序算法的逻辑结构是相同的。通过泛型编程,我们可以编写一个通用的排序函数,它可以对任何类型的数组进行排序,只要这些类型支持比较操作。

1.2 模板的引入

考虑一下,如果让你编写一个函数,用于两个数的交换。在C语言中,我们会用如下方法:

// 交换两个整型
void Swapi(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
// 交换两个双精度浮点型
void Swapd(double* p1, double* p2)
{double tmp = *p1;*p1 = *p2;*p2 = tmp;
}

上面的代码为每种可能的数据类型编写特定的代码实现,这显然显得代码非常冗余。

不过C++通过引入模板机制,使得泛型编程成为可能。

template <typename T>
void swap(T& a, T& b) {T temp = a;a = b;b = temp;
}

这个swap函数模板可以用于任何数据类型,只要这些类型支持赋值操作。

2. 函数模板

2.1 定义和使用方法

定义一个函数模板的基本语法如下:

//template <typename T>
template <class T>     //两种等价
T functionName(T parameter1, T parameter2) {// 函数体
}

其中,typename T或者class T是模板参数声明,表示这是一个模板函数,T是一个占位符,代表任何数据类型。

此外,模板中可以存在多个参数,通过, 号分隔

//多参数模板
//这里实现的是val2强制类型转换为val1,并取得和
template <class T1, class T2>
T1 getTrunVal(const T1& val1, const T2& val2)
{const T1 tmp = (const T1)val2;return val1 + tmp;
}

2.2 原理

编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
在这里插入图片描述
图示:

[ swap<T>(T&, T&)模板 ]|| 实例化V
[ swap<int>(int&, int&) ]  [ swap<double>(double&, double&) ]

下面又一个例子:

template <typename T>
void printRef(const T& value) {std::cout << value << std::endl;
}int main() {int a = 5;printRef(a); // T被推导为intconst double b = 3.14;printRef(b); // T被推导为doublestd::string c = "Hello";printRef(c); // T被推导为std::stringreturn 0;
}

2.3 实例化

函数模板的实例化可以是隐式的,也可以是显式的。当函数模板被调用时,编译器根据提供的参数自动确定模板参数的具体类型。

2.3.1 隐式实例化

考虑以下函数模板add,它的目的是返回两个参数的和

template <typename T>
T add(T a, T b) {return a + b;
}

当我们使用不同类型的参数调用add函数时,编译器会为每种参数类型生成一个专门的add函数
例如:

int main() {// 使用int类型参数调用add,编译器生成add<int>的实例int sumInt = add(1, 2);std::cout << "Sum of ints: " << sumInt << std::endl;// 使用double类型参数调用add,编译器生成add<double>的实例double sumDouble = add(1.1, 2.2);std::cout << "Sum of doubles: " << sumDouble << std::endl;// 使用string类型参数调用add,编译器生成add<string>的实例std::string sumString = add(std::string("Hello, "), std::string("World!"));std::cout << "Concatenated strings: " << sumString << std::endl;return 0;
}

在上面的例子中,我们没有显式地指定模板参数类型;编译器根据函数调用中提供的参数类型自动推导出了模板参数的类型
这种方式极大地简化了模板的使用,使得我们可以像使用普通函数一样使用模板函数,而不必担心类型的具体细节。

2.3.2 显式实例化

尽管隐式实例化对于大多数情况已经足够使用,但在某些情况下,我们可能需要显式地指定模板参数的类型。这可以通过在函数名后添加模板参数列表来实现
如:

add<double>(1, 2)

显式实例化的一个常见用途是当函数参数的自动类型推导可能不符合我们预期时。例如,当我们想要将两个整数相加,但希望结果是double类型时,就可以使用显式实例化

int main() {// 显式指定模板参数为double,即使所有参数都是int类型double result = add<double>(3, 4);std::cout << "Result (double): " << result << std::endl;return 0;
}

在这个例子中,尽管3和4都是整数,我们通过显式实例化指定了模板参数T为double,因此add函数会将两个整数的和转换为double类型返回。

2.4 匹配规则

当多个函数模板或模板与非模板函数重载时,编译器必须选择最合适的函数进行调用。

一、一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

#include <iostream>
using namespace std;
//专门用于int类型加法的非模板函数
int Add(const int& x, const int& y)
{return x + y;
}
//通用类型加法的函数模板
template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}
int main()
{int a = 10, b = 20;int c = Add(a, b); //调用非模板函数,编译器不需要实例化int d = Add<int>(a, b); //调用编译器实例化的Add函数return 0;
}

二、对于非模板函数和同名的函数模板,如果其他条件都相同,在调用时会优先调用非模板函数,而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数,那么选择模板

#include <iostream>
using namespace std;
//专门用于int类型加法的非模板函数
int Add(const int& x, const int& y)
{return x + y;
}
//通用类型加法的函数模板
template<typename T1, typename T2>
T1 Add(const T1& x, const T2& y)
{return x + y;
}
int main()
{int a = Add(10, 20); //与非模板函数完全匹配,不需要函数模板实例化int b = Add(2.2, 2); //函数模板可以生成更加匹配的版本,编译器会根据实参生成更加匹配的Add函数return 0;
}

三、模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}
int main()
{int a = Add(2, 2.2); //模板函数不允许自动类型转换,不能通过编译return 0;
}

3. 类模板

3.1 定义类模板

定义一个类模板的基本语法如下:

template <typename T>
class ClassName {// 类定义
};

其中,typename T或者class T是模板参数声明,T是一个占位符,代表任何数据类型。

示例:一个简单的Array类模板

考虑以下Array类模板,它实现了一个动态数组,可以存储任意类型的元素:

template <typename T>
class Array {
private:T* data;size_t size;
public:Array(size_t size): size(size), data(new T[size]){}~Array() {delete[] data; }T& operator[](size_t index) { return data[index]; }const T& operator[](size_t index) const { return data[index];}size_t getSize() const { return size; }};

3.2 类模板的实例化

类模板的实例化是在编译时根据指定的数据类型生成具体类的过程。与函数模板不同,类模板的实例化必须是显式的。

示例:实例化Array类模板

Array<int> intArray(10); // 创建一个可以存储10个int的数组
Array<std::string> stringArray(5); // 创建一个可以存储5个std::string的数组

实例化总结为下:

[ Array<T>类模板 ]|| 实例化V
[ Array<int> ]  [ Array<std::string> ] 

4. 结论

C++模板是一种强大的特性,它为编写高效、灵活且类型安全的代码提供了极大的便利。通过掌握模板的基础和高级特性,程序员可以提高编程效率,同时增强代码的复用性和可维护性。

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

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

相关文章

Python进阶--下载想要的格言(基于格言网的Python爬虫程序)

注&#xff1a;由于上篇帖子&#xff08;Python进阶--爬取下载人生格言(基于格言网的Python3爬虫)-CSDN博客&#xff09;篇幅长度的限制&#xff0c;此篇帖子对上篇做一个拓展延伸。 目录 一、爬取格言网中想要内容的url 1、找到想要的内容 2、抓包分析&#xff0c;找到想…

交友系统---让陌生人变成熟悉人的过程。APP小程序H5三端源码交付,支持二开。

随着社交网络的发展和普及&#xff0c;人们之间的社交模式正在发生着深刻的变革。传统的线下交友方式已经逐渐被线上交友取而代之。而同城交友正是这一趋势的产物&#xff0c;它利用移动互联网的便利性&#xff0c;将同城内的人们连接在一起&#xff0c;打破了时空的限制&#…

[计算机提升] 备份系统:系统映像

6.3 备份系统&#xff1a;系统映像 备份系统和还原系统是一套互补的操作。 操作系统的备份就是将操作系统当前的所有数据复制到硬盘的一个空闲区域&#xff0c;以防止系统崩溃或数据丢失。还原操作则是将先前备份的数据恢复到操作系统中&#xff0c;使系统回到之前的样子&…

Matplotlib绘制炫酷散点图:从二维到三维,再到散点图矩阵的完整指南与实战【第58篇—python:Matplotlib绘制炫酷散点图】

文章目录 Matplotlib绘制炫酷散点图&#xff1a;二维、三维和散点图矩阵的参数说明与实战引言二维散点图三维散点图散点图矩阵二维散点图进阶&#xff1a;辅助线、注释和子图三维散点图进阶&#xff1a;动画效果和交互性散点图矩阵进阶&#xff1a;调整样式和添加密度图总结与展…

【小白开服日记】幻兽帕鲁服务器如何搭建?

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…

时序预测 | MATLAB实现基于CNN-BiLSTM-AdaBoost卷积双向长短期记忆网络结合AdaBoost时间序列预测

时序预测 | MATLAB实现基于CNN-BiLSTM-AdaBoost卷积双向长短期记忆网络结合AdaBoost时间序列预测 目录 时序预测 | MATLAB实现基于CNN-BiLSTM-AdaBoost卷积双向长短期记忆网络结合AdaBoost时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现…

多线程基础

线程与进程 进程 进程是对运⾏时程序的封装&#xff0c;是系统进⾏资源调度和分配的基本单位&#xff0c;实现了操作系统的并发。程序由指令和数据组成&#xff0c;但这些指令要运行&#xff0c;数据要读写&#xff0c;就必须将指令加载至 CPU&#xff0c;数据加载至内存&…

Arthas使用教程—— 阿里开源线上监控诊断产品

文章目录 1 简介2背景3 图形界面工具 arthas 阿里开源3.1 &#xff1a;启动 arthas3.2 help :查看arthas所有命令3.3 查看 dashboard3.4 thread 列出当前进程所有线程占用CPU和内存情况3.5 jvm 查看该进程的各项参数 &#xff08;类比 jinfo&#xff09;3.6 通过 jad 来反编译 …

2-1 动手学深度学习v2-Softmax回归-笔记

回归 VS 分类 回归估计一个连续值分类预测一个离散类别 从回归到多类分类 回归 单连续数值输出输出的区间&#xff1a;自然区间 R \mathbb{R} R损失&#xff1a;跟真实值的区别 分类 通常多个输出&#xff08;这个输出的个数是等于类别的个数&#xff09;输出的第 i i i…

Flink Format系列(2)-CSV

Flink的csv格式支持读和写csv格式的数据&#xff0c;只需要指定 format csv&#xff0c;下面以kafka为例。 CREATE TABLE user_behavior (user_id BIGINT,item_id BIGINT,category_id BIGINT,behavior STRING,ts TIMESTAMP(3) ) WITH (connector kafka,topic user_behavior…

机器学习——有监督学习和无监督学习

有监督学习 简单来说&#xff0c;就是人教会计算机学会做一件事。 给算法一个数据集&#xff0c;其中数据集中包含了正确答案&#xff0c;根据这个数据集&#xff0c;可以对额外的数据希望得到一个正确判断&#xff08;详见下面的例子&#xff09; 回归问题 例如现在有一个…

17:定时器编程实战

1、实验目的 (1)使用定时器来完成LED闪烁 (2)原来实现闪烁时中间的延迟是用delay函数实现的&#xff0c;在delay的过程中CPU要一直耗在这里不能去做别的事情。这是之前的缺点 (3)本节用定时器来定一个时间&#xff08;譬如0.3s&#xff09;&#xff0c;在这个定时器定时时间内…

Visual Studio 2022中创建的C++项目无法使用万能头<bits/stdc++.h>解决方案

目录 发现问题 解决办法 第一步 第二步 第三步 第四步 最后一步 问题解决 发现问题 如果大家也遇到下面这种问题&#xff0c;可能是没有include文件夹中没有bits/stdc.h 解决办法 第一步 打开一个C项目&#xff0c;鼠标移动至头文件上右击&#xff0c;选择转到文档或…

嵌入式学习之Linux入门篇笔记——10,Linux连接档概念

配套视频学习链接&#xff1a;http://【【北京迅为】嵌入式学习之Linux入门篇】 https://www.bilibili.com/video/BV1M7411m7wT/?p4&share_sourcecopy_web&vd_sourcea0ef2c4953d33a9260910aaea45eaec8 目录 1.Linux 下的连接档种类 2.什么是 inode&#xff1f; 3.什…

【HarmonyOS应用开发】HTTP数据请求(十四)

文章末尾含相关内容源代码 一、概述 日常生活中我们使用应用程序看新闻、发送消息等&#xff0c;都需要连接到互联网&#xff0c;从服务端获取数据。例如&#xff0c;新闻应用可以从新闻服务器中获取最新的热点新闻&#xff0c;从而给用户打造更加丰富、更加实用的体验。 那么…

支持534种语言,开源大语言模型MaLA-500

无论是开源的LLaMA 2还是闭源的GPT系列模型&#xff0c;功能虽然很强大&#xff0c;但对语言的支持和扩展比较差&#xff0c;例如&#xff0c;二者都是以英语为主的大模型。 为了提升大模型语言的多元化&#xff0c;慕尼黑大学、赫尔辛基大学等研究人员联合开源了&#xff0c;…

Windows 安装 MySQL 最新最简教程

Windows 安装 MySQL 最新最简教程 官网地址 https://dev.mysql.com/downloads/mysql/下载 MySQL zip 文件 配置 MySQL1、解压文件 2、进入 bin 目录 搜索栏输入 cmd 回车进入命令行 C:\Users\zhong\Desktop\MySQL\mysql-8.3.0-winx64\mysql-8.3.0-winx64\bin 注意这里是你自己…

Java图形化界面编程——Container容器 笔记

2.3 Container容器 2.3.1 Container继承体系 Winow是可以独立存在的顶级窗口,默认使用BorderLayout管理其内部组件布局;Panel可以容纳其他组件&#xff0c;但不能独立存在&#xff0c;它必须内嵌其他容器中使用&#xff0c;默认使用FlowLayout管理其内部组件布局&#xff1b;S…

物理信息神经网络(PINN): 将物理知识融合到深度学习中

物理信息神经网络&#xff08;PINN&#xff09;: 将物理知识融合到深度学习中 物理信息神经网络&#xff08;PINN&#xff09;简介PINN的工作原理PINN模型如何利用物理法则指导模型训练1. 定义物理问题和相应的物理定律2. 构建神经网络3. 定义损失函数数据误差项 (Data-fidelit…

Flask 入门6:模板继承

1. 一个网站中&#xff0c;大部分网页的模块是重复的&#xff0c;比如顶部的导航栏&#xff0c;底部的备案信息。如果在每个页面中都重复的去写这些代码&#xff0c;会让项目变得臃肿&#xff0c;提高后期的维护成本。比较好的做法是&#xff0c;通过模板继承&#xff0c;把一…