初识C++ · 模板初阶

目录

1 泛型编程

2 函数模板

3 类模板


1 泛型编程

模板是泛型编程的基础,泛型我们碰到过多次了,比如malloc函数返回的就是泛型指针,需要我们强转。

既然是泛型编程,也就是说我们可以通过一个样例来解决类似的问题,比如:

void swap(int& a,int& b)
{int tmp = a;a = b;b = tmp;
}

就交换问题来说,我们可以交换整型,可以交换浮点型,但是我们可不可以在一个文件里面实现同时交换两种类型呢?实际上对于C语言来说是不可以的,因为C语言没有模板的概念。

可能会说用typedef ,但是用了也只能解决交换其他类型的原因,不能实现同时交换两种不同的类型,也可能说用auto,但是auto不能作为函数参数使用。

这里就需要用到模板了,模板使用到了两个关键字,一个是template ,一个是typename,template是模板的英文名,typename是类型名的英文,还是很好理解的。

使用如下:

template <typename T1, typename T2,…… typename Tn>

2 函数模板

相关的关键字只有两个,这里来谈一下使用的问题,具体使用如下:

template <typename T>
//模板
void Swap(T& a,T& b)
{T tmp = a;a = b;b = tmp;
}
int main()
{int a = 1, b = 2;Swap(a, b);double c = 1.1, d = 2.2;Swap(c, d);return 0;
}

可能看起来比较抽象?

模板实现的原理是模板实例化,背后靠的都是编译器,编译器会自动推演需要的类型,所以这里会有一个问题:两次交换调用的是同一个函数吗?

当然不是的,这里可以借助汇编看一下:

汇编层面可以看到调用的函数的地址不一样,所以调用的函数不是一个函数。

模板其实是编译器在负重前面,我们使用模板的时候,编译器自动推演出我们需要的函数,

那么这里就涉及效率问题了,某种意义上来说模板的调用效率确实没有直接调用来的快,但是省事儿,我们不用再去多写那么多行代码了,毕竟函数重载也要多写代码的

基本的使用我们了解了,以后我们使用交换函数也不用自己去实现了,因为库里面有,可以直接进行使用:

现在有一些问题:

使用模板的时候,我们想要交换类型不同的怎么办?

如果我们尝试直接交换a c的话,就会报错,即没有函数模板是可以使用的

为了方便演示使用加法函数:

T Add(T x,T y)
{return x + y;
}

解决方法1->强制转换:

int main()
{int a = 1;double b = 2.5;Add(a,(int)b);return 0;
}

但是这种处理方法丢失了原本我们期待的结果,它造成了精度的丢失。

解决方法2->显式实例化:

int main()
{int a = 1;double b = 2.5;double ret = Add<double>(a,b);cout << ret;return 0;
}
int main()
{int a = 1;double b = 2.5;cout << Add<int,double>(a, b) << endl;return 0;
}

 显式实例化可以让编译器不去自动推演参数类型。

我们知道最后的结果是double类型的,所以显式调用double类型的加法函数,调用的写法是函数后面加个<>,里面写要显式调用的类型,但是呢,都差点意思。

解决方法3->多个模板混用 + auto:

template <typename T1,typename T2>
auto Add(const T1& x,const T2& y)
{return x + y;
}
int main()
{int a = 1;double b = 2.5;cout << Add(a, b) << endl;return 0;
}

既然是多参数,那么我们使用不同的模板就行,但是返回值的话,就用auto即可,这里可以说auto很妙,不会存在丢失精度的问题,也不用显式实例化,也不用强转什么的。

但是有些情况,是必须要显式实例化的:

T* Func(int a)
{T* p = (T*)operator new(sizeof(T));new(p)T(a);return p;
}

返回的是一个指针,但是函数返回什么类型呢?如果没有显式实例化的话是没有办法返回的。

这里就必须要显式实例化了:

int main()
{int a = 1;Func<int>(a);return 0;
}

再补充一个小点,typename可以用class进行代替,二者完全等价的。

template <typename T>
template <class T>

两种写法均可。

这里在引入一个点,叫匹配原则,有合适的优先选择合适的:

//整型的加法函数
int Add(int x,int y)
{cout << "int Add(int x,int y)" << endl;return x + y;
}
//通用加法函数
template <typename T1,typename T2>
auto Add(const T1& a,const T2& b)
{cout << "auto Add(const T1& a,const T2& b)" << endl;return a + b;
}
//一般加法
template<typename T>
T Add(const T& left, const T& right)
{cout << "T Add(const T& left, const T& right)" << endl;return left + right;
}

这里提供了三个函数可以实现加法,一个是类型完全匹配的,一个是通用的加法函数,一个是需要编译器需要自己推演的:

int main()
{cout << Add(1, 3) << endl;cout << Add(1, 3.3) << endl;cout << Add(1.1, 3.1) << endl;return 0;
}

那么这里的调用,就会:

第一个,因为完全匹配非模板的函数,所以优先调用;

第二个,因为更匹配通用的加法函数,需要编译器自己推演的力度没有那么大,所以调用两个模板的参数。

第三个,完全需要编译器自己推理,那就只能它了。

所以函数模板的调用也要看谁更匹配,优先使用更匹配的,编译器也想休息~


3 类模板

类模板其实后面用的最多的,现在先做个了解,比如stack,我们要实现两种栈,一个是用来存储int数据,一个是用来存储数据double的,就需要用到模板,不然解决不了一个文件存在两个类型栈的情况:

template<typename T>
class Stack
{
public:Stack(size_t capacity = 4){_array = (T*)malloc(sizeof(T) * capacity);if (nullptr == _array){perror("malloc申请空间失败");return;}_capacity = capacity;_size = 0;}void Push(const T& data);
private:T* _array;size_t _capacity;size_t _size;
};

有了模板之后,我们实现相同的就很容易了,这里如果里面的函数定义和声明分离的话,如果不是同一个.h文件的话,造成的效果是很难想象的,会造成编译的时间大幅度的增加。

分离之后的写法:

template<typename T>
class Stack
{
public:Stack(size_t capacity = 4){_array = (T*)malloc(sizeof(T) * capacity);if (nullptr == _array){perror("malloc申请空间失败");return;}_capacity = capacity;_size = 0;}void Push(const T& data);
private:T* _array;size_t _capacity;size_t _size;
};
template<class T>
void Stack<T>::Push(const T& data)
{;
}

创建不同的Stack就显式实例化就可以了:

int main()
{stack<int> p1;stack<double> p2;return 0;
}

模板初阶还是好理解的,后面介绍高阶的。


感谢阅读!

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

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

相关文章

leetcode1290-Convert Binary Number in a Linked List to Integer

题目 给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。 请你返回该链表所表示数字的 十进制值 。 示例 1&#xff1a; 输入&#xff1a;head [1,0,1] 输出&#xff1a;5 解释&#xff1a;二进制数 (101) 转化为…

Java基础之《mybatis-plus多数据源配置》

1、pom文件引入依赖 引入MyBatis-Plus之后请不要再次引入MyBatis以及mybatis-spring-boot-starter和MyBatis-Spring&#xff0c;以避免因版本差异导致的问题 <!--引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 mybatis-spring-boot-starter和MyBatis-Spring&#xff0…

【C++】STL_ string的使用 + 模拟实现

前言 目录 1. STL简介&#xff08;1&#xff09;什么是STL&#xff08;2&#xff09;STL的版本&#xff08;3&#xff09;STL的六大组件 2. string的使用2.1 npos2.2 遍历字符串string的每一个字符2.3 迭代器&#xff1a;2.4 string的内存管理2.5 string模拟实现2.5.1 深拷贝&a…

Redis(主从复制搭建)

文章目录 1.主从复制示意图2.搭建一主多从1.搭建规划三台机器&#xff08;一主二从&#xff09;2.将两台从Redis服务都按照同样的方式配置&#xff08;可以理解为Redis初始化&#xff09;1.安装Redis1.yum安装gcc2.查看gcc版本3.将redis6.2.6上传到/opt目录下4.进入/opt目录下然…

iptables---防火墙

防火墙介绍 防火墙的作用可以理解为是一堵墙&#xff0c;是一个门&#xff0c;用于保护服务器安全的。 防火墙可以保护服务器的安全&#xff0c;还可以定义各种流量匹配的规则。 防火墙的作用 防火墙具有对服务器很好的保护作用&#xff0c;入侵者必须穿透防火墙的安全防护…

第V章-Ⅰ Vue3路由vue-router初识

第V章-Ⅰ Vue3路由vue-router初识 安装Vue路由基础router-link 组件导航router-view 路由出口单独导入关于路由的库文件定义路由组件定义路由规则对象创建router实例将路由对象挂载Vue实例上redirect 路由重定向嵌套路由 路由传参params形式传参query形式传参params方式与query…

Leetcode—1991. 找到数组的中间位置【简单】

2024每日刷题&#xff08;129&#xff09; Leetcode—1991. 找到数组的中间位置 实现代码 class Solution { public:int findMiddleIndex(vector<int>& nums) {int sum accumulate(nums.begin(), nums.end(), 0);int prefix 0;for(int i 0; i < nums.size();…

考情分析 | 2025年西北工业大学计算机考研考情分析!

西北工业简称西工大&#xff08;英文缩写NPU&#xff09;&#xff0c;大学坐落于古都西安&#xff0c;是我国唯一一所以同时发展航空、航天、航海工程教育和科学研究为特色&#xff0c;以工理为主&#xff0c;管、文、经、法协调发展的研究型、多科性和开放式的科学技术大学。十…

代码随想录-算法训练营day33【贪心算法03:K次取反后最大化的数组和、加油站、分发糖果】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第八章 贪心算法 part03● 1005.K次取反后最大化的数组和 ● 134. 加油站 ● 135. 分发糖果 详细布置 1005.K次取反后最大化的数组和 本题简单一些&#xff0c;估计大家不用想着贪心 &#xff0c;用自己直觉也会有…

怎么制作好玩的gif?试试这个工具轻松制作

视频之所以受大众的喜爱是因为有声音、画面的搭配&#xff0c;让观者深入其中体验感会更强。但是视频的体积较大、时长也比较长&#xff0c;给我们的传播和保存造成了一定的影响。那么&#xff0c;我们可以将视频制作成gif图片来使用&#xff0c;不需要下载软件&#xff0c;使用…

最大数字——蓝桥杯十三届2022国赛大学B组真题

问题分析 这道题属于贪心加回溯。所有操作如果能使得高位的数字变大必定优先用在高位&#xff0c;因为对高位的影响永远大于对低位的影响。然后我们再来分析一下&#xff0c;如何使用这两种操作&#xff1f;对于加操作&#xff0c;如果能使这一位的数字加到9则变成9&#xff0…

LeetCode-hot100题解—Day6

原题链接&#xff1a;力扣热题-HOT100 我把刷题的顺序调整了一下&#xff0c;所以可以根据题号进行参考&#xff0c;题号和力扣上时对应的&#xff0c;那么接下来就开始刷题之旅吧~ 1-8题见LeetCode-hot100题解—Day1 9-16题见LeetCode-hot100题解—Day2 17-24题见LeetCode-hot…

UE5自动生成地形一:地形制作

UE5自动生成地形一&#xff1a;地形制作 常规地形制作地形编辑器地形管理添加植被手动修改部分地形的植被 置换贴图全局一致纹理制作地貌裸露岩石地形实例 常规地形制作 地形制作入门 地形导入部分 选择模式&#xff1a;地形模式。选择地形子菜单&#xff1a;管理->导入 …

STC8增强型单片机开发——C51版本Keil环境搭建

一、目标 了解C51版本Keil开发环境的概念和用途掌握C51版本Keil环境的安装和配置方法熟悉C51版本Keil开发环境的使用 二、准备工作 Windows 操作系统Keil C51 安装包&#xff08;可以从Keil官网下载&#xff09;一款8051单片机开发板 三、搭建流程 环境搭建的基本流程&#xf…

思维导图网页版哪个好?2024年值得推荐的8个在线思维导图软件!

思维导图如今已成为一种常用的工具&#xff0c;帮助我们清晰地组织和整理信息。随着科技的发展&#xff0c;思维导图的产品形态也经过多轮迭代&#xff0c;从最初的本地客户端过渡到基于云的 Web 端&#xff0c;各类网页版思维导图软件应运而生&#xff0c;它们方便快捷&#x…

【Linux】gcc/g++的使用

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; Linux &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解Linux中gcc/g使用的相关内容。 如果看到最后您觉得这篇文章写得不错…

【Linux】CAN根据时钟频率、波特率计算采样点详解

1、采样点知识回顾 参考博客:【CAN】知识点:帧类型、数据帧结构、传输速率、位时间、采样点 CAN 采样点是指在一个数据位的传输周期内,接收器实际采样数据的时间点。这个时间点是以百分比来表示的,它决定了在数据位的传输周期中,何时读取数据位的值。 正确设置采样点对…

js api part3

环境对象 环境对象&#xff1a; 指的是函数内部特殊的 变量 this &#xff0c; 它代表着当前函数运行时所处的环境 作用&#xff1a; 弄清楚this的指向&#xff0c;可以让我们代码更简洁 函数的调用方式不同&#xff0c;this 指代的对象也不同 【谁调用&#xff0c; this 就是…

Qt | QLineEdit 类(行编辑器)

01、上节回顾 Qt | QComboBox(组合框)02、QLineEdit 1、QLineEdit 类是 QWidget 类的直接子类,该类实现了一个单行的 输入部件,即行编辑器,见右图 2、验证器(QValidator 类)和输入掩码简介:主要作用是验证用户输入的字符是否符合验证器 的要求,即限制对用户的输入,比…

【C#_变量_格式化输出_If语句_微信猜拳_第一篇】

C#&#xff08;编程入门&#xff09; 练习来自腾讯课堂免费课程1.1.基本输出语句基本输出语句——知识点总结Console.Clear();清屏字符串和数字区分Console.ReadLine(); 1.2.变量的基本使用&#xff1a;变量的基本使用——知识点总结 1.3.格式化输出格式化输出——知识点总结 1…