c++的学习之路:19、模板

摘要

本章主要是说了一些模板,如非类型模板参数、类模板的特化等等,文章末附上测试代码与导图

目录

摘要

一、非类型模板参数

二、类模板的特化

1、概念

2、函数模板特化

3、类模板特化

三、模板的分离编译

1、什么是分离编译

2、模板的分离编译

四、模板总结

1、优点

2、缺点

五、代码

1、test.cpp

2、Date.h 

3、Date.cpp

六、导图

一、非类型模板参数

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

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

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

类型模板在之前写代码的时候经常使用,就是template<class T>在之前代码中经常使用这个,这个就是模板可以让编译器自动推演类型,从而方便使用,如下方代码如果想要定义一个数组指定他的大小,在以前我是用宏定义的,但是如果需要修改的话还是挺麻烦的,还是需要一个一个去更改也会很麻烦这时,c++提出了一个该念叫做非类型模板。

#define n 10
namespace ly
{
    template<class T>
    class arry
    {
    private:
        int    arr[n];
    };
}

非类型模板 就是如下方代码这种给一个缺省值就可以利用这个缺省值去进行初始化数组的就是非类型模板,但是需要注意一下两点:

1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。

2. 非类型的模板参数必须在编译期就能确认结果。

template<class T,size_t n=10>
    class arry
    {
    private:
        int    arr[n];
    };

二、类模板的特化

1、概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板,这里就不得不说说之前所用到的仿函数,在优先队列那里实现的代码就用了仿函数,但是有些情况就需要提出说一下,如下方代码和下方图片我想要的结果是小于,结果他却比较了指针大小这就不是我想要的,这时就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。

template<class T>
    bool Less(T left, T right)
    {
        return left < right;
    }    

cout << ly::Less(1, 2) << endl;
    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);
    cout << ly::Less(d1, d2) << endl;
    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << ly::Less(p1, p2) << endl;

2、函数模板特化

函数模板的特化步骤:

1. 必须要先有一个基础的函数模板

2. 关键字template后面接一对空的尖括号<>

3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型

4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误

下面的代码就是特化的写法,这个写法是祖师爷规定的,他的形式就是这种的。

template<>
    bool Less<Date*>(Date* left, Date* right)
    {
        return *left < *right;
    }

3、类模板特化

类模板特化又分为全特化和偏特化,偏特化就是部分特化,全特化就是全部特化,如下方测试的代码所示。

下方这个代码就是全特化,全特化就是把参数全部都进行实例化,如下方带就是实例化成了int和char这样编译器就更加适配这个就不会在进行自动推演生成了。

namespace ly
{
    template<class T1, class T2>
    class Data
    {
    private:
        T1 _d1;
        T2 _d2;
    };
    template<>
    class Data<int, char>
    {
    private:
        int _d1;
        char _d2;
    };
}

int main()
{
    ly::Data<int, int> d1;
    ly::Data<int, char> d2;
}

 偏特化也就是部分特化,这种只进行部分的实例化,就是偏特化的使用方式。

template<class T1>
    class Data<T1, char>
    {

    private:
        int _d1;
        char _d2;
    };

三、模板的分离编译

1、什么是分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

也就是之前经常用的头文件与c文件分离使用,这个就是一个分离编译使用,相当于c语言中说的低耦合,也就是模块化。

2、模板的分离编译

但是在写c++的时候除了写日期类的时候用的还是分离,在后面写模板的时候就没有分离了,因为在我当时试了,报错搞了半天也没解决,我上网去查就查出来了一种解决方发,模板定义的位置显式实例化,但是特别麻烦,所以我就选择放在一个文件夹里面,然后我去看了一下stl的源码,代码如下,如图一就是放在类里面定义的,它定义的也就是少的就是放在类里,相当于内联长的也是放在外面写,如下方图二代码所示。

 

四、模板总结

1、优点

1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生

2. 增强了代码的灵活性

2、缺点

1. 模板会导致代码膨胀问题,也会导致编译时间变长

2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

五、代码

1、test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
#include <vector>using namespace std;//namespace ly
//{
//	template<class T,size_t n=10>
//	class arry
//	{
//	private:
//		int	arr[n];
//	};
//	template<class T>
//	bool Less(T left, T right)
//	{
//		return left < right;
//	}
//	template<>
//	bool Less<Date*>(Date* left, Date* right)
//	{
//		return *left < *right;
//	}
//}
//
//int main()
//{
//	cout << ly::Less(1, 2) << endl;
//	Date d1(2022, 7, 7);
//	Date d2(2022, 7, 8);
//	cout << ly::Less(d1, d2) << endl;
//	Date* p1 = &d1;
//	Date* p2 = &d2;
//	cout << ly::Less(p1, p2) << endl;
//}namespace ly
{template<class T1, class T2>class Data{private:T1 _d1;T2 _d2;};/*template<>class Data<int, char>{private:int _d1;char _d2;}*/template<class T1>class Data<T1, char>{private:int _d1;char _d2;};
}int main()
{ly::Data<int, int> d1;ly::Data<int, char> d2;
}

2、Date.h 

#pragma onceclass Date
{
public:// 获取某年某月的天数int GetMonthDay(int year, int month);// 全缺省的构造函数Date(int year = 1, int month = 1, int day = 1);// 拷贝构造函数Date(const Date& d);// 赋值运算符重载Date& operator=(const Date& d);// 析构函数~Date();// 日期+=天数Date& operator+=(int day);// 日期+天数Date operator+(int day);// 日期-天数Date operator-(int day);// 日期-=天数Date& operator-=(int day);// 前置++Date & operator++();// 后置++Date operator++(int);// 后置--Date operator--(int);// 前置--Date& operator--();// >运算符重载bool operator>(const Date& d);// ==运算符重载bool operator==(const Date& d);// >=运算符重载bool operator >= (const Date& d);// <运算符重载bool operator < (const Date& d);// <=运算符重载bool operator <= (const Date& d);// !=运算符重载bool operator != (const Date& d);// 日期-日期 返回天数int operator-(const Date& d);//打印void Print();
private:int _year;int _month;int _day;
};

3、Date.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "Date.h"
#include <iostream>
#include <string>
#include <vector>using namespace std;// 全缺省的构造函数
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}
// 拷贝构造函数
Date::Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}
// 析构函数
Date::~Date()
{_year = 0;_month = 0;_day = 0;
}
// 赋值运算符重载
Date& Date::operator=(const Date& d)
{if (this!=&d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}
// 日期+=天数
Date& Date::operator+=(int day)
{if (day < 0){return *this -= (-day);}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);++_month;if (_month == 13){++_year;_month = 1;}}return *this;
}
// 日期+天数
Date Date::operator+(int day)
{Date tmp(*this);tmp += day;return tmp;
}
// 日期-天数
Date Date::operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}
// 日期-=天数
Date& Date::operator-=(int day)
{if (day < 0){return *this += (-day);}_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}
// 前置++
Date& Date::operator++()
{*this += 1;return *this;
}
// 后置++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}
// 后置--
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}
// 前置--
Date& Date::operator--()
{*this -= 1;return *this;
}
// <运算符重载
bool Date::operator < (const Date& d)
{if (_year < d._year){return true;}else if (_year == d._year && _month < d._month){return true;}else if (_year == d._year && _month == d._month && _day < d._day){return true;}else{return false;}
}
// ==运算符重载
bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}
// <=运算符重载
bool Date::operator <= (const Date& d)
{return *this < d || *this == d;
}
// !=运算符重载
bool Date::operator != (const Date& d)
{return !(*this == d);
}
// >运算符重载
bool Date::operator>(const Date& d)
{return !(*this <= d);
}
// >=运算符重载
bool Date::operator >= (const Date& d)
{return !(*this < d);
}
// 日期-日期 返回天数
int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month)
{static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 };int day = days[month];if (month == 2&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))){day += 1;}return day;
}
//打印
void Date::Print()
{cout << "Print:" << _year << '/' << _month << '/' << _day << endl << endl;
}

六、导图

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

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

相关文章

2024.4.8力扣每日一题——使数组连续的最少操作数

2024.4.8 题目来源我的题解方法一 去重排序滑动窗口 题目来源 力扣每日一题&#xff1b;题序&#xff1a;2009 我的题解 方法一 去重排序滑动窗口 参考官方题解。 记数组 nums的长度为 n。经过若干次操作后&#xff0c;若数组变为连续的&#xff0c;那么数组的长度不会改变&…

ip地址切换器安卓版,保护隐私,自由上网

在移动互联网时代&#xff0c;随着智能手机和平板电脑的普及&#xff0c;移动设备的网络连接变得愈发重要。为了满足用户在不同网络环境下的需求&#xff0c;IP地址切换器安卓版应运而生。本文将以虎观代理为例&#xff0c;为您详细解析IP地址切换器安卓版的功能、应用以及其所…

UVA1596 Bug Hunt 找Bug 解题报告

题目链接 https://vjudge.net/problem/UVA-1596 题目大意 输入并模拟执行一段程序&#xff0c;输出第一个bug所在的行。每行程序有两种可能&#xff1a; 数组定义&#xff0c;格式为arr[size]。例如a[10]或者b[5]&#xff0c;可用下标分别是0&#xff5e;9和0&#xff5e;4…

Linux压缩打包

压缩文件有时候也叫归档文件&#xff0c;但是归档是将多个文件捆绑成一个文件&#xff0c;并没有压缩&#xff0c;压缩才是将大小压缩的更小。 tar 压缩 tar -zcf 压缩后文件名.tar.gz 需要压缩的文件 [rootlocalhost ~]# tar -zcf ser.tar.gz services压缩多个文件 [rootloca…

克服与新一代人工智能部署相关的数据挑战

随着商界领袖逐渐了解该技术的力量和潜力&#xff0c;人们对 ChatGPT 等生成式人工智能工具的潜力的兴趣正在迅速上升。 这些工具能够创建以前属于人类创造力和智力领域的输出&#xff0c;有潜力改变许多业务流程&#xff0c;并成为每个人&#xff08;从作家和创作者到程序员和…

题目:学习使用按位异或 ^

题目&#xff1a;学习使用按位异或 ^ There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog content is all parallel goods. Those who are worried about being cheated should leave q…

蓝桥杯加训

1.两只塔姆沃斯牛&#xff08;模拟&#xff09; 思路&#xff1a;人和牛都记录三个数据&#xff0c;当前坐标和走的方向&#xff0c;如果人和牛的坐标和方向走重复了&#xff0c;那就说明一直在绕圈圈&#xff0c;无解 #include<iostream> using namespace std; const i…

openstack-认证服务

整个OpenStack是由控制节点&#xff0c;计算节点&#xff0c;网络节点&#xff0c;存储节点四大部分组成。 openstack重要集成组件: Nova-计算服务&#xff1b;Neutron-网络服务&#xff1b;Swift-对象存储服务&#xff1b;Cinder-块存储服务&#xff1b;Glance-镜像服务Keys…

LeetCode-118. 杨辉三角【数组 动态规划】

LeetCode-118. 杨辉三角【数组 动态规划】 题目描述&#xff1a;解题思路一&#xff1a;Python 动态规划解题思路二&#xff1a;解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&…

算法学习系列(四十五):DFS之剪枝与优化

目录 引言DFS之剪枝与优化一、小猫爬山二、木棒三、数独四、总结 引言 关于这个 D F S DFS DFS 的剪枝和优化确实难度是非常的大&#xff0c;从我这篇文章的思路和代码量上就能看出来不是一般的难度&#xff0c;而且难度不亚于 D P DP DP &#xff0c;而且这个 D F S DFS D…

Go语言支持重载吗?如何实现重写?

Go语言不支持传统意义上的函数和方法重载。在Go语言中&#xff0c;函数名或方法名不能相同但参数列表不同&#xff0c;因为这会导致编译错误。 然而&#xff0c;可以通过方法重写&#xff08;override&#xff09;来实现类似的功能。方法重写是指在子类中定义一个与父类同名的…

C语言进阶课程学习记录-第27课 - 数组的本质分析

C语言进阶课程学习记录-第27课 - 数组的本质分析 数组实验-数组元素个数的指定实验-数组地址与数组首元素地址实验-指针与数组地址的区别小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 数组 实验-数…

Hot100【十一】:编辑距离

// 定义dp[i][j]: 表示word1前i个字符转换到word2前j个字符最小操作数 // 初始化dp[m1][n1] class Solution {public int minDistance(String word1, String word2) {int m word1.length();int n word2.length();// 1. dp数组int[][] dp new int[m 1][n 1];// 2. dp数组初…

分布式系统接口限流方案

方案一、 Guava工具包 实现单机版限流 Demo的Git地址&#xff1a;https://gitee.com/deepjava/test-api-limit.git 使用Google的Guava工具包提工单 RateLimiter类 可以实现单机状态下的接口限流 RestController RequestMapping("/test") public class ApiLimitCon…

IO流:将文件从A复制到B,并实现复制过程进度条的实现

private static boolean copyFile(String strFileA, String strFileB) {// 使用try资源块 ,其中创建的流对象可以自动关闭try (FileInputStream inputStream new FileInputStream(strFileA); // 输入流FileOutputStream outputStream new FileOutputStream(strFileB) // 输…

【Linux】进程的状态(运行、阻塞、挂起)详解,揭开孤儿进程和僵尸进程的面纱,一篇文章万字讲透!!!!进程的学习②

目录 1.进程排队 时间片 时间片的分配 结构体内存对齐 偏移量补充 对齐规则 为什么会有对齐 2.操作系统学科层面对进程状态的理解 2.1进程的状态理解 ①我们说所谓的状态就是一个整型变量&#xff0c;是task_struct中的一个整型变量 ②.状态决定了接下来的动作 2.2运行状态 2.…

自动化运维(十)Ansible 之进程管理模块

Ansible的进程管理模块提供了一种强大而灵活的方式来管理和操作各种进程管理器和服务。无论你使用的是Supervisor、Systemd、传统的init脚本还是Runit,这些模块都可以帮助你轻松地管理服务的生命周期。通过合理地使用这些模块,你可以实现服务的自动化管理,提高系统的可靠性和稳…

【闲聊】-网页划词翻译插件

英文之痛 作为程序猿&#xff0c;常常需要接触外文网站&#xff0c;以前很痛苦&#xff0c;现在大模型时代有很多智能工具可以直接翻译&#xff0c;翻译的虽然越来越好&#xff0c;但是还是不如直接看英文能理解本义&#xff0c;相信我&#xff0c;看翻译的理解和看原文的理解…

龙迅LT2611UXC 2 PORT LVDS桥接到HDMI 2.0,内置MCU,颗自行操作

龙迅LT2611UXC描述&#xff1a; LT2611UXC是一个高性能的LVDS到HDMI2.0的转换器&#xff0c;用于STB&#xff0c;DVD应用程序。LVDS输入可以配置为单端口或双端口&#xff0c;有1个高速时钟通道&#xff0c;3~4个高速数据通道&#xff0c;最大运行1.2Gbps/通道&#xff0c;可支…

gpu模拟器总体流程

1、开显存空间&#xff0c;初始化 这里显存就是运行模拟器的机器 2、创建页表&#xff0c;开设备端空间并复制数据 虚拟地址 3、划分形状&#xff0c;传入内核函数&#xff0c;形状参数和设备端数据地址、执行计算 4、复制数据回主机端&#xff0c;释放gpu资源