【C++修炼秘籍】模板(下)
🌸心有所向,日复一日,必有精进
🌸专栏《C++修炼秘籍》
🌸作者:早凉
目录
【C++修炼秘籍】模板(下)
文章目录
前言
一、非类型的模板参数
二、模板的特化
三、模板分离编译
总结
前言
泛型编程的有力支撑——模板,在上次的文章中已经可以使用起来,也对STL的学习有了很大作用,本文会再次将一些细小知识汇总作为参考。
一、非类型的模板参数
类型模板,T是类型,在下方的场景中,如果我们想在类中开辟数组,如果通过宏,我们就必须修改宏;
template<class T>//是类型模板
#define N 10
class Array
{
private:
T a;
T a[N];
}
int main()
{
Array<int> a1;
Array<double>a2;
}
N是一个非类型的模板参数,用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
template<class T ,size_t N = 10>//后后边就是非类型怒版参数
{
private:
T _arry;
T _a[N];
}
int main()
{
array<int,10>a2;
int a3[10];
}
在库里,C++11中支持的一个静态数组,这里就是实现非类型的模板参数。
❓但是C语言就有静态数组支持啊?为什么库里还要在C++11里还要实现什么?
诶,我们发现我们这不是越界访问了吗?问什么编译器没报错啊,这是很危险的啊,
这时我们发现,array是强制检查的。
原生数组对于越界的检查:越界读不检查;越界写会抽查
array对于越界的检查::array只要越界就检查,比起原生数组更加严格;
❗️ 注意
1、浮点数、类对象,以及字符串是不允许作为非类型的模板参数的;
2、非类型的模板参数必须在编译器就能确认结果;
二、模板的特化
函数模板
针对某些类型进行特殊处理例如:日期类
template<class T>
bool Less(T left,T right)
{
return left<right;
}
//是上面那个特殊处理针对Date的特化
template<>
bool Less<Date*>(Date*left,Date*right)
{
return *left<*right;
}
//构成重载也是可以
bool Less(Date*left,Date*right)
{
return *left<*right;
}
Date d1();
Date d1();
cout<<Less(d1,d2)<<endl;
Date* p1 = &d1;
Date* p2 = &d2;
cout<<Less(p1,p2)<<endl;
类模板
类模板就不能写个函数了,只能特化;
template<class T,class N>
class D{
public:
D(){
cout<<"T,N"<<endl;
}
private:
T t;
N n;
}
//特化全特化
template<>
class D<double,double>{
public:
D(){
cout<<"double,double"<<endl;
}
private:
}
半特化/偏特化
template<class T>
class D<T,char>{
public:
D(){
cout<<"T,char"<<endl;
}
private:
}
参数类型进一步限制 (也属于半特化)
template<class T,class N>
class D<T*,N*>
{
public:
D(){cout<<"T*,N*"<<endl;}
}
template<class T,class N>
class D<T&,N&>
{
public:
D(){cout<<"T&,N&"<<endl;}
}
❗️ 体现匹配原则:有全特化就优先全特化,其次半特化,最后模板;
三、模板分离编译
声明和定义分离是有大问题的 :
//template.h
#pragma once
template<class T>
T Add(const T& left, const T& right);
///template.h///
#include"template.h"
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
///test.h//
#include<iostream>
using namespace std;
#include"template.h"
int main()
{
Add(11, 22);
Add(3.2, 2.1);
return 0;
}
发现这里我们找不到Add函数呀,这是因为 在add.cpp中在没有传参的时候不会对Add模板实例化,因此不会产生具体的函数,在main.obj中调用Add<int>在编译器链接是才会找地址,但是这俩函数没有实例化成具体代码,因此链接报错。
❓如何解决?
1、将声明和定义放到一个文件 ;
2、模板定义的位置显式实例化;;
template int Add<int>(const int&, const int&);
template double Add<double>(const double&, const double&);
上述是函数模板,那么类模板该怎么办呢?
/.h/
#pragma once
template <class T>
class CSwap{
public:
CSwap(T a, T b);
void Swap();
public:
T _a;
T _b;
};
/CSwap.cpp///
#include"template.h"
template <class T>
void CSwap<T>::Swap()
{
T tmp = _a;
_a = _b;
_b = tmp;
}
template <class T>
CSwap<T>::CSwap(T a, T b)
{
_a = a; _b = b;
}
///test.cpp/
#include<iostream>
using namespace std;
#include"template.h"
int main()
{
CSwap<int> c(1,2);
c.Swap();
cout << c._a << " " << c._b << endl;
}
看着熟悉的报错!那么类模板怎么解决呢?——在CSwap.cpp文件中加入:
template
class CSwap<int>;
总结
模板的缺点
分离编译,链接之前不会交互,没有实例化;模板的报错特别**,就是会给人打电报的感觉,摸不到头脑;模板会导致代码膨胀问题,也会导致编译时间变长
优点:
模板复用了代码,节省资源,更快的迭代开发,产生了C++的标准模板库(STL)
增强了代码的灵活性
如果觉得有帮助的话,可以点赞 + 收藏 + 评论支持一下哦!这对我的帮助很大~:)