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 once

class 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;
}

六、导图

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一次网卡驱动BUG故障的排错历程

前言 在日常运维中&#xff0c;总会遇到一些棘手的故障或问题&#xff0c;尤其面临多系统融合的兼容性或一些融合节点可能存在未知bug等方面&#xff0c;排错难度都会增加。 本文将从一次小事件为入口进行延伸&#xff0c;将宿主机esxi基础系统的多融合节点故障的排错历程展开…

分布式监控平台---Zabbix

一、Zabbix概述 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果&#xff0c;和网站的健康状态。 利用一个优秀的监控软件&#xff0c;我们可以&#xff1a; 通过一个友好的界面进行浏览整个…

C/C++ C/C++ 入门(6)模板初阶

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;C 多多指教&#xff01; 一、泛型编程 在之前&#xff0c;我们进行编程的时候&#xff0c;总是针对于某一个具体的问题。就比如说&#xff0c;如何实现一个int类型的swap函数呢&#xff1f;大家肯定会写。…

MySQL 表管理

目录 建库 语法&#xff1a; 库名命名规则&#xff1a; 相关命令&#xff1a; 建表 语法&#xff1a; 相关命令&#xff1a; 修改表 语法&#xff1a; 常用操作命令 复制表 数据类型 MySQL的10种常用数据类型&#xff1a; 数据的导入和导出 导入&#xff1a; 格…

iptables 学习

文章目录 iptables 学习iptables基本组件&#xff1a;常用iptables命令&#xff1a;iptables -L 输出及解释解释&#xff1a; iptables “奇淫巧技”端口转发&#xff08;port forwarding&#xff09;流量重定向到透明代理防止DDoS攻击防止SYN洪泛攻击黑名单使用状态模块跟踪连…

第14届java A组蓝桥杯做题记录

A题 特殊日期 package Java14省赛.Java研究生组;import java.time.Year; //特殊判断一下2月份&#xff0c;leaf 为true 1 import java.util.*;import 蓝桥杯.dfs_n皇后; public class 特殊日期 {static int sum(int d){int res 0;while(d > 0){res d % 10;d / 10;}return…

基于Springboot+Vue的Java项目-房产销售系统(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

FlexLua低代码便捷打造4G转RS485网关设备

在物联网时代&#xff0c;各种设备之间的互联互通变得越来越重要&#xff0c;而4G转RS485网关设备的出现为不同设备之间的通信提供了更便捷的方式&#xff0c;推动了物联网技术的发展。 4G转RS485网关的通信原理相对简单易懂。它通过4G网络接收数据&#xff0c;然后将数据转换成…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之十三 简单去除图片水印效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之十三 简单去除图片水印效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之十三 简单去除图片水印效果 一、简单介绍 二、简单去除图片水印效果实现原理 三、简单去除图片水印效果案例…

Java入门基础知识第八课(数组)——冒泡排序、Arrays工具类

前面二白讲了关于数组的概念、语法以及简单的输入输出&#xff0c;实际上关于数组的知识还有很多&#xff0c;接下来咱们讲一下冒泡排序以及一些常用的Arrays工具类&#xff0c;需要记忆的知识很多&#xff0c;而且容易混淆。 一、冒泡排序 简介&#xff08;原理&#xff09;…

基于SpringBoot的“银行OA系统的设计与实现”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“银行OA系统的设计与实现”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 用户登录界面 管理员主界面 员工基本档…

知道做到 一篇总结学习方法的笔记

元数据 [!abstract] 知道做到&#xff1a;跃迁式学习 书名&#xff1a; 知道做到&#xff1a;跃迁式学习作者&#xff1a; 彼得•霍林斯简介&#xff1a; 学习是改善你的生活环境、成为你想成为的人的关键。科学的方法能加速学习进程&#xff0c;让你事半功倍。技能、信息和能力…

openkylin系统通过网线连接ubuntukylin系统上网攻略

openkylin系统通过网线连接ubuntukylin系统上网攻略 主机1&#xff1a;x64 amd &#xff0c;系统&#xff1a;ubuntukylin 22.04 &#xff0c;状态&#xff1a;通过wifi连接热点进行上网&#xff0c;并共享网络。 主机2&#xff1a;x64 intel &#xff0c;系统&#xff1a;ope…

5.x 版本 CallKit SDK 无法弹起通话界面

5.x 版本 CallKit SDK 作为接听方在前台的情况下无法弹起通话界面&#xff0c;作为拨打方能正常弹起通话界面 分析&#xff08;根因分析、需求分析&#xff09; CallKit SDK 初始化依赖于 IMKit SDK 初始化&#xff0c;如果您使用 IMLib SDK 的方法初始化的话&#xff0c;会导…

告别百年激进笔记

系列文章目录 八次危机笔记 告别百年激进笔记 文章目录 系列文章目录前言导图第一部分 资本全球化的宏大叙事第一节 人类创造的两个异化物第二节 全球资本化与制度性致贫第三节 国家竞争的“微笑曲线”第四节 欧债危机实属政治危机第五节 日本研究中的另类思考第六节 从…

流程图的新语法-mermaid的快速使用--推荐

chatgpt或者现在的大数据采用的流程图给出的代码如下&#xff1a; graph TD;A[接收客户请求] --> B[问题分类];B --> C[技术支持];B --> D[维修服务];C --> E[远程解决];C --> F[现场支持];D --> G[维修完成];G --> H[服务反馈];style A fill:#f9f,strok…

【数据结构与算法】贪心算法及例题

目录 贪心算法例题一&#xff1a;找零问题例题二&#xff1a;走廊搬运物品最优方案问题输入样例例题三&#xff1a;贪心自助餐 贪心算法 贪心算法是一种在每一步选择中都采取当前状态下最优的选择&#xff0c;以期望最终达到全局最优解的算法。它的核心思想是每次都选择当前最…

即插即用模块详解SCConv:用于特征冗余的空间和通道重构卷积

目录 一、摘要 二、创新点说明 2.1 Methodology 2.2SRU for Spatial Redundancy​编辑 2.3CRU for Channel Redundancy 三、实验 3.1基于CIFAR的图像分类 3.2基于ImageNet的图像分类 3.3对象检测 四、代码详解 五、总结 论文&#xff1a;https://openaccess.thecvf.c…

kafka的概念以及Zookeeper集群 + Kafka集群 +elfk集群

目录 zookeeper同步过程 分布式通知和协调 zookeeper同步过程 分布式通知和协调 准备 3 台服务器做 Zookeeper 集群 192.168.68.5 192.168.68.6 192.168.68.7 安装前准备 //关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 node1服务器&a…

Linux进阶篇:性能监控工具:socket 统计信息

Linux性能监控工具&#xff1a;socket 统计信息 1 ss命令介绍 ss 是 Socket Statistics 的缩写。ss 命令可以用来获取 socket 统计信息&#xff0c;它显示的内容和 netstat 类似。但 ss 的优势在于它能够显示更多更详细的有关 TCP 和连接状态的信息&#xff0c;而且比 netsta…