【C++初阶】模板

⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:C++初阶
⭐代码仓库:C++初阶
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!

模板

  • 前言
  • 一、模板参数分类
    • 1、介绍
    • 2、定长数组的非类型模板参数
    • 2、利用类型形参进行不同容器适配打印
    • 3、介绍typename
  • 二、模板的特化
    • 1、概念
    • 2、函数模板特化
    • 3、类模板特化
      • (1)全特化
      • (2)偏特化
        • i、部分特化
        • ii、参数更进一步的限制
    • 4、总结
  • 三、模板分离编译
    • stack和普通函数的例子
      • (1)func1通过和fun2崩溃的原因
      • (2)size()通过的原因
      • (3)push()和pop()崩溃的原因
        • 三种解决方法:
          • 1、显示实例化
          • 2、同一文件
          • 3、.hpp方式
  • 四、模板总结


前言

模板进阶体现了很多的不同的方法和思想,这里提供了不同的分类和特化以及分离编译,我们需要掌握百分之八九十并在后面慢慢打磨不断历练。


一、模板参数分类

1、介绍

模板参数分类类型形参与非类型形参。
类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参:是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

// 非模板常量的优势:传多少就是多少很方便
// 非模板常量的局限:1、必须是整型 2、是一个常量
template<class T, size_t N> // N是一个常量 不能被修改
class Stack
{
public:
private:T _a[N];int _top;
};int main()
{Stack<int, 10> st1; // 10个空间Stack<int, 100> st2; // 100个空间return 0;
}
  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
  2. 非类型的模板参数必须在编译期就能确认结果。

2、定长数组的非类型模板参数

我们来简单了解一个定长数组,就是用这个非类型模板参数来解决的。

这是简单的数组:
在这里插入图片描述

这是定长数组:
在这里插入图片描述
虽然看起来没什么问题,但是最奇怪的一个点为为什么定长数组不初始化所有的呢?这个就是委员会没有进行更新的原因了。那么定长数组唯一的优势是检查越界很有用,我们看下面的代码:
在这里插入图片描述

2、利用类型形参进行不同容器适配打印

#include<iostream>
#include<vector>
#include<list>
using namespace std;template<class Container>
void Print(const Container& v)
{// 编译不确定Container::const_iterator是类型还是对象// typename就是明确告诉编译器这是类型,等模板实例化再去找typename Container::const_iterator it = v.begin();// auto是类型所以可以用auto//auto it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;
}int main()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout << e << " ";}cout << endl;Print(v);list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);Print(lt);return 0;
}

设计一个容器,我们在main函数中设计简单的容器并将其传到Print函数中,Print函数中利用模板自行去寻找相对应的容器进行输出打印。

3、介绍typename

以前我们讲typename和class一样,但是这里我们讲一个区别的点,当我们分不清一个代码到底是类型还是对象还是变量的时候,我们加一个typename告诉编译器这个是类型,不是对象,因为编译器没那么聪明,加入进来一个静态的变量呢?编译器根本分不清,如果编译器茫然地去寻找,那肯定是不符合规定的。而编译器知道了这是个类型,会在类模板实例化的时候自动去找这个容器类型的实现规则并进行操作。


二、模板的特化

1、概念

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

在这里插入图片描述
我们发现,我们传参数过去比较的是地址大小,没有实现我们的目的,所以我们有了一个模板的特化帮助我们进行实现传参的比较。

2、函数模板特化

函数模板的特化步骤:

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

在这里插入图片描述

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

在这里插入图片描述
该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化。

3、类模板特化

(1)全特化

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

在这里插入图片描述

(2)偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类:

i、部分特化

将模板参数类表中的一部分参数特化。

在这里插入图片描述

ii、参数更进一步的限制

偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。

在这里插入图片描述

4、总结

template<class T1, class T2>
class Data
{
public:Data() { cout << "Data(T1, T2)" << endl; }
};// 全特化
template<>
class Data<int, double>
{
public:Data() { cout << "Data(int, double)" << endl; }
};// 偏特化 -- 特化部分参数
// 可能对某些类型的进一步限制
template<class T1>
class Data<T1, double>
{
public:Data() { cout << "Data(T1, double)" << endl; }
};// 偏特化 -- 特化成指针
template<class T1, class T2>
class Data<T1*, T2*>
{
public:Data() { cout << "Data(T1*, T2*)" << endl; }
};//两个参数偏特化为引用类型
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;
};int main()
{Data<int, int> d1;Data<int, double> d2;Data<int*, double> d3;Data<double, double> d4;return 0;
}

在这里插入图片描述


三、模板分离编译

stack和普通函数的例子

举个简单的例子:
mobel.h:

#include<iostream>
#include<vector>
#include<deque>namespace JRH
{// 容器适配器template<class T, class Container = std::deque<T>>class stack{public:// 插入 尾插void push(const T& x);// 删除 尾删void pop();// 取栈顶元素T& top(){return _con.back();}// 输出个数size_t size(){return _con.size();}// 判空bool empty(){return _con.empty();}private:Container _con;};class A{public:void func1();void func2();};
}

mobel.cpp:

#include"mobel.h"namespace JRH
{template<class T, class Container>// 插入 尾插void stack<T, Container>::push(const T& x){_con.push_back(x);}template<class T, class Container>void stack<T, Container>::pop(){_con.pop_back();}void A::func1(){}}

test.cpp:

#include<iostream>
#include<vector>
#include<list>
#include<array>
using namespace std;#include"mobel.h"int main()
{JRH::stack<int> st;st.push(1); // 崩溃st.push(2); // 崩溃st.pop();   // 崩溃st.size();  // 通过JRH::A a;a.func1(); // 通过a.func2(); // 崩溃return 0;
}

在这里插入图片描述

我们分析一下main函数中有些函数是通过的有些函数是崩溃的原因:

(1)func1通过和fun2崩溃的原因

func1是在汇编的时候将定义的func1函数转化成地址指令,然后在链接的时候直接找到func1定义的地址并进行链接,链接成功,能够通过运行。

func2没有定义,自然在链接的时候找不到定义的地址,链接不成功直接崩溃。

(2)size()通过的原因

size()本来就能够通过,因为它的定义和声明不是分离的,是合在一起的,在编译的时候直接能找到了。

(3)push()和pop()崩溃的原因

push()和pop()因为有个模板参数,也就是在汇编的时候转化成为xxpushi(?)与xxpushxx(?)不同而我们的定义是一个模板!没有进行实例化!它没有指定类型是int还是double还是char,它并不知晓是什么,同样也没有地址。所以链接失败。

三种解决方法:

1、显示实例化

一种巧妙的方法:使用模板显示实例化(不推荐)。

mobel.cpp:
在这里插入图片描述

2、同一文件

还有一种最好的方法,stl也是这样做的就是将声明定义分离在同一文件中:
在这里插入图片描述

3、.hpp方式

最后一种好的方法是:
将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。


四、模板总结

【优点】

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。
  2. 增强了代码的灵活性 例如适配器和仿函数。

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长。
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误。

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

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

相关文章

亚马逊云科技纽约峰会,充分释放数据价值和生成式AI的潜力

生成式AI将深刻改变每个公司的运营方式&#xff0c;标志着人工智能技术发展的新转折点。亚马逊云科技昨日在纽约峰会上宣布&#xff0c;推出七项生成式AI新功能&#xff0c;进一步降低了生成式AI的使用门槛&#xff0c;让无论是业务用户还是开发者都能从中受益。借助这些新功能…

【解析excel】利用easyexcel解析excel

【解析excel】利用easyexcel解析excel POM监听类工具类测试类部分测试结果备注其他 EasyExcel Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存&#xff0c;poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题&…

2、基于redis实现分布式锁

目录 2.1. 基本实现2.2. 防死锁2.3. 防误删2.4. redis中的lua脚本2.4.1 redis 并不能保证2.4.2 lua介绍 2.5. 使用lua保证删除原子性 2.1. 基本实现 借助于redis中的命令setnx(key, value)&#xff0c;key不存在就新增&#xff0c;存在就什么都不做。同时有多个客户端发送setn…

亿信华辰举行制造业数字化转型研讨会,解密数字化最佳实践

制造业是国家经济命脉所系&#xff0c;推进制造业数字化转型已成为发展数字经济的重中之重。今天&#xff08;5月9日&#xff09;上午&#xff0c;亿信华辰携手沙丘社区成功举办“制造业数字化转型研讨会”&#xff0c;1.3万人线上观看&#xff0c;汇聚华为、鼎捷软件、亿信华辰…

uniapp实现预约时间选择弹窗组件

做了个组件&#xff0c;实现出当日预约时间组件&#xff0c;效果图如下 废话不多说&#xff0c;直接上代码&#xff0c;代码简单&#xff0c;参数自己任意改 <template><view class"inventory"><u-popup :show"show" :round"10"…

OpenAI重磅官宣ChatGPT安卓版本周发布,现已开启下载预约,附详细预约教程

7月22号&#xff0c;OpenAI 突然宣布&#xff0c;安卓版 ChatGPT 将在下周发布&#xff01;换句话说&#xff0c;本周安卓版 ChatGPT正式上线&#xff01; 最早&#xff0c;ChatGPT仅有网页版。 今年5月&#xff0c;iOS版ChatGPT正式发布&#xff0c;当时OpenAI表示Android版将…

【C++ 重要知识点总结】自定义类型-类和结构体

类 类的基本特性 数据抽象和封装继承多态 1 类的构成——抽象 概念 数据抽象是一种依赖于接口和实现的分离的编程技术。类的接口包括用户所能执行的操作&#xff1b;类的实现包括类的数据成员、负责接口实现的函数体以及定义类所需要的的各种私有函数。封装实现了类的接口和实…

Java使用FFmpeg实现mp4转m3u8

Java使用FFmpeg实现mp4转m3u8 前言FFmpegM3U8 一、需求及思路分析二、安装FFmpeg1.windows下安装FFmpeg2.linux下安装FFmpegUbuntuCentOS 三、代码实现1.引入依赖2.修改配置文件3.工具类4.Controlle调用5.Url转换MultipartFile的工具类 四、播放测试1.html2.nginx配置3.效果展示…

Web3.0:已经开启的互联网革命!

1 痛点 2 web发展形态 只读、封闭式、协作式。 3 一个高度联系、全球统一的数字经济体 去中心化架构通过计算几余打破数据垄断&#xff0c;同时实现数字确权大量的功能依靠智能合约自动实现&#xff0c;运转效率大大提升DAO大量涌现&#xff0c;全球范围实现资源配置 4 特…

【Element-ui】学习与使用

网站快速成型工具Element&#xff0c;一套为开发者、设计师和产品经理准备的基于vue2.0的桌面端组件库 安装 npm i element-ui -S 在项目中安装element-ui&#xff0c;安装了以后查看package.json中的依赖中有没有element-ui的版本&#xff0c;如果有&#xff0c;则说明安装成功…

Spring Boot 自定义启动画面

文章目录 自定 Banner获取属性设置颜色实操关闭 Banner参考 我们启动项目的之后&#xff0c;会在控制台上看到类似下面的画面&#xff1a; 那么&#xff0c;我们是否可以自定义呢&#xff1f; 肯定可以 自定 Banner 上面的截图信息就是 Banner 信息&#xff0c;我们可以在项目…

在Chrome(谷歌浏览器)中安装Vue.js devtools开发者工具及解决Vue.js not detected报错

文章目录 一、Vue.js devtools开发者工具安装1.打开谷歌浏览器——点击扩展程序——选择管理扩展程序2.先下载添加一个谷歌助手到扩展程序中&#xff08;根据提示进行永久激活&#xff09;3.点击谷歌浏览器的应用商店4.输入Vue.js devtools——搜索——选择下载 二、解决Vue.js…

如何为WordPress博客网站配置免费域名HTTPS证书

文章目录 如何为WordPress博客网站配置免费域名HTTPS证书前置条件&#xff1a;步骤1 申请免费的域名HTTPS证书步骤2 将HTTP证书配置到cpolar的配置文件中2.1 创建证书文件夹2.2 修改cpolar配置文件2.3 重启cpolar服务2.4 查看后台Wordpress隧道是否在线正常2.5 用浏览器打开站点…

MyBatis基础模块-类型转换模块

文章目录 1. 为什么需要类型转换模块2. TypeHandler 1. 为什么需要类型转换模块 执行sql&#xff0c;在PreparedStatement设置参数时&#xff0c;需要把java类型转换成jdbc类型&#xff0c;而从结果集中获取数据时&#xff0c;需要把jdbc类型转换为java类型。 2. TypeHandle…

Linux操作系统~必考面试题⑧

1、pwd 命令 pwd 命令用于查看当前工作目录路径。 实例&#xff1a; 查看当前路径 pwd 查看软链接的实际路径 pwd -P 2、rmdir 命令 从一个目录中删除一个或多个子目录项&#xff0c;删除某目录时也必须具有对其父目录的写权限。 注意&#xff1a;不能删除非空目录实例&…

Linux系统MySQL中用户的权限管理

本节主要学习用户权限管理的概述&#xff0c;用户权限类型&#xff0c;用户赋权&#xff0c;权限删除&#xff0c;用户删除等。 目录 一、概述 二、用户权限类型 三、用户赋权 四、权限删除 五、用户删除 一、概述 数据库用户权限管理是数据库系统中非常重要的一个方面&am…

统一观测丨使用 Prometheus 监控 Cassandra 数据库最佳实践

作者&#xff1a;元格 本篇内容主要包括四部分&#xff1a;Cassandra 概览介绍、常见关键指标解读、常见告警规则解读、如何通过 Prometheus 建立相应监控体系。 Cassandra 简介 Cassandra 是什么&#xff1f; Apache Cassandra 是一个开源、分布式、去中心化、弹性可伸缩、…

Vue3封装函数组件(ElImageViewer)预览图片

目录结构 index.vue <template><el-image-viewer v-if"show" v-bind"$attrs" hide-on-click-modal close"show false" /> </template><script setup> import { ref, watch } from "vue" import { ElImageV…

Linux基础以及常用命令

目录 1 Linux简介1.1 不同应用领域的主流操作系统1.2 Linux系统版本1.3 Linux安装1.3.1 安装VMWare1.3.2 安装CentOS镜像1.3.3 网卡设置1.3.4 安装SSH连接工具1.3.5 Linux和Windows目录结构对比 2 Linux常用命令2.0 常用命令&#xff08;ls&#xff0c;pwd&#xff0c;cd&#…

mysql的一些知识整理

这里整理一些mysql相关的知识点&#xff0c;是自己不太熟悉的内容 varchar(n) 中 n 最大取值为多少 MySQL 规定除了 TEXT、BLOBs 这种大对象类型之外&#xff0c;其他所有的列&#xff08;不包括隐藏列和记录头信息&#xff09;占用的字节长度加起来不能超过 65535 个字节。 …