C++入门小馆: 模板

嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的passion。准备好和我一起冲进代码的奇幻宇宙了吗?Let's go!

我的博客:yuanManGan

我的专栏:C++入门小馆 C言雅韵集 数据结构漫游记  闲言碎语小记坊 题山采玉 领略算法真谛

目录

模板:

1.泛型编程:

2.函数模板

2.1函数模板概念:

2.2函数模板格式:

2.3 函数模板的原理

2.4 函数模板的实例化

1.隐式实例化:

2. 显式实例化:

2.5 模板参数的匹配原则

3. 类模板

3.1 类模板的定义格式

3.2 类模板的实例化

4. 非类型模板参数

5.模板的特化

5.1函数模板的特化:

5.2类模板特化

5.2.1全特化

5.2.2 偏特化

6 模板分离编译

7. 模板总结


模板:

1.泛型编程:

我们为什么要出现模板呢?

我们来看看下列个这个函数:

void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}
void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}
void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}
......

我们要实现交换,难道写无数个类型的组合?

这里用函数重载有很多缺点:

代码的可维护性差,多一个类型就要多写几个对应的代码,复用率低

那我们是否可以告诉编译器一个模子,让编译器自动生成对应的函数呢?

模板又分为函数模板和类模板:

2.函数模板

2.1函数模板概念:

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生
函数的特定类型版本。

2.2函数模板格式:

template<typename T1, typename T2, ......, typename Tn>
返回值类型  函数名(参数列表){ }

注意: typename 用来定义模板参数 关键字 也可以使用 class( 切记:不能使用 struct 代替
class)

2.3 函数模板的原理

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

2.4 函数模板的实例化

用不同类型的参数使用函数模板时 ,称为函数模板的 实例化 。模板参数实例化分为: 隐式实例化
和显式实例化
1.隐式实例化:
让编译器根据实参推演模板参数的实际类型

 该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
 通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
 编译器无法确定此处到底该将T确定为int 或者 double类型而报错注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅

此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化 
Add(a1, (int)d1);
2. 显式实例化:
在函数名后的 <> 中指定模板参数的实际类型

如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错

2.5 模板参数的匹配原则

1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这
个非模板函数
我们的模板就是自己做饭吃,而非模板函数就是外卖(现成),你妈要出去,给了你钱,你会选择自己做饭吃还是点外面呢?废话点外卖啊!
当外面显式实例化后就变成了,你妈知道你要点外卖,就跟你说,儿啊不要点外卖吃,你点外卖我回来打死你,你就只能自己做饭吃了。
2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而
不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模
3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

3. 类模板

3.1 类模板的定义格式

template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
#include<iostream>
using namespace std;
// 类模版
template<typename T>
class Stack
{
public:Stack(size_t capacity = 4){_array = new T[capacity];_capacity = capacity;_size = 0;}void Push(const T& data);
private:T* _array;size_t _capacity;size_t _size;
};
// 模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误,具体原因后面会讲
template<class T>
void Stack<T>::Push(const T& data)
{// 扩容_array[_size] = data;++_size;
}
int main()
{Stack<int> st1; // intStack<double> st2; // doublereturn 0;
}

在STL中大量使用类模板。

3.2 类模板的实例化

类模板实例化与函数
模板实例化不同, 类模板实例化需要在类模板名字后跟 <> ,然后将实例化的
类型放在 <> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
// Stack是类名,Stack<int>才是类型
Stack<int> st1; // int
Stack<double> st2; // double

4. 非类型模板参数

先看看下面的

假如我们要创建a1的存储空间为100,a2的存储空间为50。但我们实例化出来的对象它的存储空间依旧是N,我们只能将就内存大的,这时就会出现空间浪费。那有没有方法我们能自己控制N是多少呢?有的兄弟有的:

模板参数分为类型形参与非类型形参

类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。

非类型形参,就是用一个常量作为类(函数)模板中可将改参数当成常量来使用。

非类型形参就可以解决这个问题:

那有没有什么实例用到了这个呢?

有的,就是动态数组array

这里没有尾部插入删除。因为我们可以直接通过下标访问最后的元素。

那你说这个和 array<int, 100> a 和 int a[100]有什么区别呢?

有人说array可以自定义类型比如:

array<string, 100> a;

那我问你,数组不能这样吗:

string a[20];

那我用vector不是更香吗?

array和静态数组没有什么本质区别,非要说区别就是array有迭代器,但静态数组也可以使用范围for,本质的类型的转换,然后,array对越界的检查更严格:

越界读越界访问
静态数组不报错抽查报错
array报错报错
注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的
2. 非类型的模板参数必须在编译期就能确认结果

5.模板的特化

我们看看下面的代码:

通常情况下, 使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些
错误的结果 ,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

我们比较指针的时候是无法比较出我们想要的结果,那我们该怎么做呢,可以在Date类里面重载一下比较运算符当传入的是指针的时候特殊处理一下。

那我们就是不想改变原类呢,怎么在模板上进行修改呢?

这里就可以使用我们的模板的特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方。模板特化中分为函数模板特化类模板特化

5.1函数模板的特化:

函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字 template 后面接一对空的尖括号 <>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇
怪的错误。

 注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该 函数直接给出。

 直接写函数比写模板的特化要舒服很多。

bool Less(Date* left, Date* right)
{return *left < *right;
}
该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化
时特别给出,因此函数模板不建议特化。

5.2类模板特化

5.2.1全特化

全特化即是将模板参数列表中所有的参数都确定化。

template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};
template<>
class Data<int, char>
{
public:Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};
void TestVector()
{Data<int, int> d1;Data<int, char> d2;
}

这全特化出来的对象就和非模板函数和模板函数的关系一样,有更匹配的调用更匹配的,没有就匹配的就调用模板重新创建一个。

5.2.2 偏特化
偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};
部分特化:

我们可以对其中一个或多个参数特化

template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};
// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};

参数限制:

还可以指定传入的类型,比如传入指针就走这种类型:

//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:Data() { cout << "Data<T1*, T2*>" << endl; }
private:T1 _d1;T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout << "Data<T1&, T2&>" << endl;}
private:const T1& _d1;const T2& _d2;
};

6 模板分离编译

我们之前学习STL时说过使用了模板之后的类的函数声明和定义最好不要分离。

我们先来了解一下这些:

这个是我们之前学习c语言时可能会困惑的问题,如果在头文件写声明和定义会出现链接报错。

因为在符号表中会重复包含add函数,test.cpp和func.cpp共用一个符号表。我们可以将声明和定义分离到不同的头文件,或者将函数静态不出现在符号表。

我们将声明的定义分离是怎么实现的呢,这里再来回顾一下:

7. 模板总结

【优点】
1. 模板复用了代码,节省资源,更快的迭代开发, C++ 的标准模板库 (STL) 因此而产生
2. 增强了代码的灵活性
【缺陷】
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

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

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

相关文章

强化学习之基于无模型的算法之基于值函数的深度强化学习算法

3、基于值函数的深度强化学习算法 1&#xff09;深度Q网络&#xff08;DQN&#xff09; 核心思想 DQN是一种将Q学习与深度神经网络结合的方法&#xff0c;用于解决高维状态空间的问题。 它以环境的状态作为输入&#xff0c;通过神经网络输出每个动作的 Q 值&#xff0c;智能体…

网络规划和设计

1.结构化综合布线系统包括建筑物综合布线系统PDS&#xff0c;智能大夏布线系统IBS和工业布线系统IDS 2.GB 50311-2016综合布线系统工程设计规范 GB/T 50312-2016综合布线系统工程验收规范 3.结构化布线系统分为6个子系统&#xff1a; 工作区子系统&#xff1b;水平布线子系…

软件设计师-错题笔记-计算机硬件和体系

1. 解析&#xff1a;循环冗余校验码也叫CRC校验码&#xff0c;其中运算包括了模2&#xff08;异或&#xff09;来构造校验位。别的三种没有用到模2的方法。 2. 解析&#xff1a;如果是正数&#xff0c;则是首位为0&#xff0c;其余位全为1&#xff0c;这时最大数(2^(n-1))-1…

OpenCV 4.7企业级开发实战:从图像处理到目标检测的全方位指南

简介 OpenCV作为工业级计算机视觉开发的核心工具库,其4.7版本在图像处理、视频分析和深度学习模型推理方面实现了显著优化。 本文将从零开始,系统讲解OpenCV 4.7的核心特性和功能更新,同时结合企业级应用场景,提供详细代码示例和实战项目,帮助读者掌握从基础图像处理到复…

LeetCode算法题 (除自身以外数组的乘积)Day14!!!C/C++

https://leetcode.cn/problems/product-of-array-except-self/description/ 一、题目分析 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀…

如何写好Verilog状态机

还记得之前软件的同事说过的一句话。怎么凸显自己的工作量&#xff0c;就是自己给自己写BUG。 看过夏宇闻老师书的都知道&#xff0c;verilog的FSM有moore和mealy,然后有一段&#xff0c;二段&#xff0c;三段式。记得我还是学生的时候&#xff0c;看到这里的时候&#xff0c;感…

晶振频率/稳定度/精度/温度特性的深度解析与测量技巧

在电子设备的精密世界里&#xff0c;晶振如同跳动的心脏&#xff0c;为各类系统提供稳定的时钟信号。晶振的频率、稳定度、精度以及温度特性&#xff0c;这些关键参数不仅决定了设备的性能&#xff0c;更在不同的应用场景中发挥着至关重要的作用。 一、频率选择的本质&#xff…

Kafka-可视化工具-Offset Explorer

安装&#xff1a; 下载地址&#xff1a;Offset Explorer 安装好后如图&#xff1a; 1、下载安装完毕&#xff0c;进行新增连接&#xff0c;启动offsetexplorer.exe&#xff0c;在Add Cluster窗口Properties 选项下填写Cluster name 和 kafka Cluster Version Cluster name (集…

LabVIEW模板之温度监测应用

这是一个温度监测应用程序&#xff0c;基于 Continuous Measurement and Logging 示例项目构建&#xff0c;用于读取模拟温度值&#xff0c;当温度超出给定范围时发出警报 。 这个。 详细说明 运行操作&#xff1a;直接运行该 VI 程序。点击 “Start” 按钮&#xff0c;即可开…

后端[特殊字符][特殊字符]看前端之Row与Col

是的&#xff0c;在 Ant Design 的栅格布局系统中&#xff0c;每个 <Row> 组件确实对应页面上的一个独立行。以下是更详细的解释&#xff1a; 核心概念 组件作用类比现实场景<Row>横向容器&#xff0c;定义一行内容类似 Excel 表格中的一行<Col>纵向分割&am…

[特殊字符] SpringCloud项目中使用OpenFeign进行微服务远程调用详解(含连接池与日志配置)

&#x1f4da; 目录 为什么要用OpenFeign&#xff1f; 在cart-service中整合OpenFeign 2.1 引入依赖 2.2 启用OpenFeign 2.3 编写Feign客户端 2.4 调用Feign接口 开启连接池&#xff0c;优化Feign性能 3.1 引入OkHttp 3.2 配置启用OkHttp连接池 3.3 验证连接池生效 Feign最佳…

VARIAN安捷伦真空泵维修清洁保养操作SOP换油操作流程内部转子图文并茂内部培训手侧

VARIAN安捷伦真空泵维修清洁保养操作SOP换油操作流程内部转子图文并茂内部培训手侧

【android bluetooth 案例分析 03】【PTS 测试 】【PBAP/PCE/SSM/BV-10-C】

1. PBAP/PCE/SSM/BV-10-C [PCE Does not share PbapSupportedFeatures bits] 这个 PTS 测试用例 PBAP/PCE/SSM/BV-10-C 的核心目的是验证 PBAP 客户端&#xff08;PCE&#xff09;在与旧版服务器通信时&#xff0c;不会发送 PbapSupportedFeatures 特性位&#xff0c;以确保兼…

批量删除OpenStack实例

在Linux终端实现批量删除OpenStack实例&#xff0c;支持并发删除、安全确认、重试机制、优先清理运行中实例 #!/bin/bash # # 增强版 OpenStack 删除实例脚本 # 功能&#xff1a;支持并发删除、安全确认、重试机制、优先清理运行中实例 # 更新&#xff1a;2025年4月30日 # ##…

# 基于 Python 和 jieba 的中文文本自动摘要工具

基于 Python 和 jieba 的中文文本自动摘要工具 在信息爆炸的时代&#xff0c;快速准确地提取文本核心内容变得至关重要。今天&#xff0c;我将介绍一个基于 Python 和 jieba 的中文文本自动摘要工具&#xff0c;帮助你高效地从长文本中提取关键信息。 一、背景与需求 在处理…

Seaborn数据可视化库

一、Seaborn介绍&#xff1a;基于Matplotlib的Python数据可视化库&#xff0c;专注绘制统计图形&#xff0c;简化可视化过程&#xff0c;提供高级接口和美观默认主题。 二、安装与导入 1.安装&#xff1a;可使用pip install seaborn或conda install seaborn&#xff0c;也可使…

机器视觉2D码垛和机器视觉3D码垛的区别

机器视觉3D码垛是一种结合3D视觉技术和工业机器人的自动化系统,主要用于在复杂环境中精准识别、定位并堆叠(码垛)各种形状、尺寸的物体。它通过3D传感器(如激光雷达、结构光相机、双目视觉等)获取物体的三维空间信息,并结合算法规划机器人的抓取路径和码放策略,实现高效…

Python魔法函数深度解析

一、魔法函数是什么&#xff1f; 魔法函数&#xff08;Magic Methods&#xff09;是Python中以双下划线&#xff08;__xx__&#xff09;包裹的特殊方法&#xff0c;它们为类提供了一种与Python内置语法深度集成的能力。这些方法由解释器自动调用&#xff0c;无需显式调用&…

C++负载均衡远程调用学习之自定义内存池管理

目录 1.内存管理_io_buf的结构分析 2.Lars_内存管理_io_buf内存块的实现 3.buf总结 4.buf_pool连接池的单例模式设计和基本属性 5.buf_pool的初始化构造内存池 6.buf_pool的申请内存和重置内存实现 7.课前回顾 1.内存管理_io_buf的结构分析 ## 3) Lars系统总体架构 ​ …

流水线问题(算法设计)C++

目录 一、需求分析 1.1 问题描述 1.2 数据需求 1.3 功能需求 1.4 开发环境 二、概要设计 2.1 抽象数据类型 ADT 的定义 2.2 系统的主要功能模块 2.3 功能模块联系图 三、详细设计 3.1 数据结构设计 3.2 主要算法 四、系统运行及结果分析 1. 用户界面 2. 程序运行…