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

发布于:2024-04-09 ⋅ 阅读:(98) ⋅ 点赞:(0)

摘要

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

目录

摘要

一、非类型模板参数

二、类模板的特化

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

六、导图


网站公告

今日签到

点亮在社区的每一天
去签到