C++底层学习预备:模板初阶
文章目录
- 1.编程范式
- 2.函数模板
- 2.1 函数模板概念
- 2.2 函数模板原理
- 2.3 函数模板实例化
- 2.3.1 隐式实例化
- 2.3.2 显式实例化
- 2.4 模板参数的匹配原则
- 3.类模板
- 希望读者们多多三连支持
- 小编会继续更新
- 你们的鼓励就是我前进的动力!
进入STL库学习之前我们要先了解有关模板的学习,以便在学习完STL库使用之后,能更深入的了解其底层工作原理
1.编程范式
编程范式
指的是我们使用编程的基本风格和方法
常见的方式有以下几种:
面向对象编程(OOP)
将数据和操作数据的方法封装在类中,通过类的实例(对象)来进行交互,强调数据的封装、继承和多态性
定义一个Shape
基类,包含计算面积的纯虚函数,再派生出Circle
和Rectangle
等类,重写计算面积的函数,体现了面向对象的继承
和多态
特性
函数式编程
将计算视为函数的组合和应用,强调不可变数据和纯函数,避免副作用,注重函数的输入输出关系
使用std::function
和lambda
表达式可以方便地进行函数式编程,如用lambda
表达式定义一个简单的加法函数
,不修改外部状态,只返回计算结果
过程式编程
以过程(函数)为中心,将程序分解为一系列的步骤和函数调用,数据和操作数据的函数相对独立
传统的C语言
风格的编程方式,如编写一个计算阶乘的函数,通过循环
和递归
来实现计算过程,就是典型的过程式编程
泛型编程
定义函数、类或其他程序结构时,不指定具体的数据类型,而是使用类型参数来代表未知的数据类型
在algorithm
头文件中的swap
函数就是一种常见的泛式编程,他不指定任何类型就能实现交换,依靠的就是泛式编程,也是我们接下来要学习的模板
2.函数模板
在还不知道头文件前实现swap函数
通常是这样的:
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.1 函数模板概念
我们知道文字的印刷是依靠活字印刷术的模板实现的,那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?
这里用到的模板就是函数模板
,其语法形式为:
template<typename T1, typename T2,......,typename Tn>
template
就是模板的意思,是用来定义模板参数关键字,也可以使用class
,切记:不能使用struct
代替class
,因为struct
和class
的默认权限不同,会导致一些混淆和潜在的问题
2.2 函数模板原理
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器
举个例子:
template<typename T>
void Swap(T& a, T& b)
{T temp = a;a = b;b = temp;
}
实现一个Swap交换函数
对两个不同类型的函数进行同一个函数的调用,调试模式下转到反汇编可以发现,两个函数式模板示例化后被调用的
这直接说明了调用的不是同一个函数
在编译器编译阶段
,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数
以供调用。
比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码
,这个类型无论是内置类型
还是自定义类型
都可以
2.3 函数模板实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化
2.3.1 隐式实例化
让编译器根据实参推演模板参数的实际类型
叫作隐式实例化
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);return 0;
}
正常情况下的调用就是隐式实例化
🔥值得注意的是: Add函数前加const是因为这里如果像下面例子一样进行强制转化会生成临时变量,具有常性
该知识点在前面有提到过:
传送门:C++命运石之门代码抉择:C++入门(中)
2.3.2 显式实例化
在函数名后的<>中指定模板参数的实际类型
叫作显式实例化
Add(a1, d1);
还是上面的例子,如果既调用int,又调用double,到底是用哪种类型编译器无法决定
,就需要显式实例化
🚩用户自己来强制转化
Add(a1, (int)d1);
🚩使用显式实例化
Add<int>(a1, d1);
指定T的类型为int
这通常不是显式实例化的常用场景,举个例子:
template<class T>
T* Alloc(int n)
{return new T[n];
}int main()
{Alloc<int>(5);return 0;
}
如果写成Alloc(5)
,编译器不知道你要分配的是int数组
、double数组
还是其他类型的数组,所以无法自动推导T的类型
,这时候就需要显式指定模板参数,像Alloc<int>(5)
这样明确告诉编译器T是int类型
2.4 模板参数的匹配原则
🚩一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
// 专门处理int的加法函数
int Add(int left, int right)
{return left + right;
}// 通用加法函数
template<class T>
T Add(T left, T right)
{return left + right;
}void Test()
{Add(1, 2); // 与非模板函数匹配,编译器不需要特化Add<int>(1, 2); // 调用编译器特化的Add版本
}
🚩对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
// 专门处理int的加法函数
int Add(int left, int right)
{return left + right;
}// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{return left + right;
}void Test()
{Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}
🚩模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
这里的自动转化就是上面的实例化中的转化,也要和auto自动推导区分开,不是同一个东西
3.类模板
类模板其实和函数模板是类似的
其语法形式为:
template<class T1, class T2, ..., class Tn>
因为类不像函数那样语法上支持自动类型转化,所以类模板调用必须显式实例化
// 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}// 使用析构函数演示:在类中声明,在类外定义。~Vector();void PushBack(const T& data);void PopBack();// ...size_t Size() { return _size; }T& operator[](size_t pos){assert(pos < _size);return _pData[pos];}private:T* _pData;size_t _size;size_t _capacity;
};
// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{if (_pData)delete[] _pData;_size = _capacity = 0;
}int main()
{// Vector类名,Vector<int>才是类型Vector<int> s1;Vector<double> s2;return 0;
}
我们在写模板类时尽量不要声明定义分离
,原因有些复杂放在模板进阶的时候讲,如果一定分离的话要注意:
- 对于
普通类
,类名和类型一样 - 对于
模板类
,Vector类名
,Vector<int>才是类型
希望读者们多多三连支持
小编会继续更新
你们的鼓励就是我前进的动力!
相关文章:

C++底层学习预备:模板初阶
文章目录 1.编程范式2.函数模板2.1 函数模板概念2.2 函数模板原理2.3 函数模板实例化2.3.1 隐式实例化2.3.2 显式实例化 2.4 模板参数的匹配原则 3.类模板希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力! 进入STL库学习之前我们要先了解有关模板的…...

使用mybatisPlus插件生成代码步骤及注意事项
使用mybatisPlus插件可以很方便的生成与数据库对应的PO对象,以及对应的controller、service、ImplService、mapper代码,生成这种代码的方式有很多,包括mybatis-plus提供的代码生成器,以及idea提供的代码生成器,无论哪一…...

扩散模型(二)
相关阅读:扩散模型(一) Parameterization of L t L_t Lt for Training Loss 回想一下,我们需要训练一个神经网络来近似反向扩散过程中的条件概率分布,即, p θ ( x t − 1 ∣ x t ) N ( x t − 1 ; μ θ ( x t…...

java异常处理——try catch finally
单个异常处理 1.当try里的代码发生了catch里指定类型的异常之后,才会执行catch里的代码,程序正常执行到结尾 2.如果try里的代码发生了非catch指定类型的异常,则会强制停止程序,报错 3.finally修饰的代码一定会执行,除…...

新月军事战略分析系统使用手册
新月人物传记: 人物传记之新月篇-CSDN博客 相关故事链接:星际智慧农业系统(SAS),智慧农业的未来篇章-CSDN博客 “新月智能武器系统”CIWS,开启智能武器的新纪元-CSDN博客 “新月之智”智能战术头盔系统&…...
Docker Hub 镜像 Pull 失败的解决方案
目录 引言一、问题二、原因三、解决方法四、参考文献 引言 在云原生技术火热的当下,Docker可谓是其基础,由于其简单以及方便性,让开发人员不必再为环境配置问题而伤脑筋,因为可将其看作一个虚拟机程序去理解。所以掌握好它可谓是…...
SQL进阶实战技巧:如何构建用户行为转移概率矩阵,深入洞察会话内活动流转?
目录 1 场景描述 1.1 用户行为转移概率矩阵概念 1.2 用户行为转移概率矩阵构建方法 (1) 数据收集...

DeepSeek辅助学术写作关键词选取
关键词 关键词主要从论文标题、摘要及正文中提炼出来,需要准确反映论文的核心主题和专业领域。关键词的选择不仅有助于标引人员进行主题词的选取、数据库的建立以及文献的检索,而且也便于读者高效检索和引用相关学术成果,从而促进学术交流的…...

后盾人JS -- 原型
没有原型的对象 也有没有原型的对象 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document<…...

优选算法的灵动之章:双指针专题(一)
个人主页:手握风云 专栏:算法 目录 一、双指针算法思想 二、算法题精讲 2.1. 查找总价格为目标值的两个商品 2.2. 盛最多水的容器 编辑 2.3. 移动零 2.4. 有效的三角形个数 一、双指针算法思想 双指针算法主要用于处理数组、链表等线性数据结构…...

BUUCTF Pwn axb_2019_brop64 题解
这题是BROP 所以不下文件 先nc一下看看: 先要找到栈溢出长度: from pwn import * import timedef getsize():i 1while True:try:p remote("node5.buuoj.cn", 29367)p.sendafter("Please tell me:", ba * i)time.sleep(0.1)data …...

85.[1] 攻防世界 WEB easyphp
进入靶场 属于代码审计 <?php // 高亮显示当前 PHP 文件的源代码,常用于调试或展示代码 highlight_file(__FILE__);// 初始化两个标志变量,用于后续条件判断 $key1 0; $key2 0;// 从 GET 请求中获取参数 a 和 b $a $_GET[a]; $b $_GET[b];// 检…...

动态规划学习
在进行算法题练习和一些题目中发现关于动态规划的内容较多,觉得有必要系统的学习和练习一下 于是参照bilbilUP主 英雄哪里出来 的动态规划50题和LeetKoke网站进行学习和练习 一 概述 动态规划 是一个有限状态自动机 可以抽象为一个有向无环图 有起始节点 终止节点 …...

数据结构【链栈】
基于 C 实现链表栈:原理、代码与应用 一、引言 栈就是一个容器,可以当场一个盒子,只能一个一个拿,一个一个放,而且是从上面放入。 有序顺序栈操作比较容易【会了链栈之后顺序栈自然明白】,所以我们这里只…...

软件测试02----用例设计方法
今天目标 1.能对穷举场景设计测试点 2.能对限定边界规则设计测试点 3.能对多条件依赖关系进行设计测试点 4.能对项目业务进行设计测试点 一、解决穷举场景 重点:使用等价类划分法 1.1等价类划分法 重点:有效等价和单个无效等价各取1个即可。 步骤&#…...

编程AI深度实战:给vim装上AI
系列文章: 编程AI深度实战:私有模型deep seek r1,必会ollama-CSDN博客 编程AI深度实战:自己的AI,必会LangChain-CSDN博客 编程AI深度实战:给vim装上AI-CSDN博客 编程AI深度实战:火的编程AI&…...
《DeepSeek R1:大模型最简安装秘籍》
DeepSeek R1:AI 大模型界的新起之秀 在人工智能的璀璨星空中,大模型如繁星般闪耀,而 DeepSeek R1 无疑是其中一颗冉冉升起的新星,自问世以来便吸引了全球的目光,在人工智能领域占据了重要的一席之地。 从性能表现上看…...

物业管理平台系统为社区管理带来数字化转型与服务创新新机遇
内容概要 物业管理平台系统是数字化转型的利器,为社区管理带来了许多新机遇。想象一下,传统社区物业管理中繁琐的流程和低效的沟通如何被这种智能系统所替代。通过集成在线收费功能,我们不仅提高了费用收取的准确性,还减少了业主…...
红黑树的封装
一、封装思路 在 STL 中 map set 的底层就是封装了一棵红黑树。 其中连接红黑树和容器的是迭代器,map set 暴露出的接口都不是自己写的,而是红黑树写的,外部接口封装红黑树接口。 所以写出红黑树为 map set 写的接口,再在上层的…...

25.2.3 【洛谷】作为栈的复习不错(学习记录)
今天学习的东西不算多,放了一个星期假,感觉不少东西都没那么清楚,得复习一下才行。今天搞个栈题写,把栈复习一下,明天进入正轨,边复习边学习新东西,应该会有二叉树的学习等等... 【洛谷】P1449 …...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...
大数据治理的常见方式
大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法,以下是几种常见的治理方式: 1. 数据质量管理 核心方法: 数据校验:建立数据校验规则(格式、范围、一致性等)数据清洗&…...