C模板进阶
C+模板进阶
一、非类型模板参数
#define N 10;
template<class T>
class Stack
{
private:
T _a[N];
};
Stack<int> st1;//10
Stack<int> st2;//100
你看这里N已经define写死了,但是我想让st1和st2开辟不同大小的空间怎么办,
#define N 10000;
像我们这里把N扩大到10000,但是我们又用不了这么多空间,这还会造成资源浪费,所以C++引入了一个叫非类型模板参数
template<class T,size_t N>
class Stack
{
private:
T _a[N];
};
Stack<int,10> st1;//10
Stack<int,100> st2;//100
这是一个常量,常量才能控制数组的大小,像我们输入值这种都是不行的
像如果我们是变量的时候,编译器在编译的时候要实例化,实例化我们又不知道实例化出多少,数组我们也不知到去开多大
像char,int,short这些整型家族的才可以
函数模板的特化
步骤
1.必须要先有一个基础的函数模板
2.关键字template后面接一对空的尖括号<>
3.函数名后跟一对尖括号,尖括号中指定需要特化的类型
4.函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
二、类模板的特化
模板特化:针对某些类型进行特殊化处理
template<class T1, class T2>
class Data
{
public:
Data()
{
cout << "Data<T1,T2>" << endl;
}
private:
T1 d1;
T2 d2;
};
template<>
class Data<int,double>
{
public:
Data()
{
cout << "Data<int,double>" << endl;
}
};
Data<int, int> d1;
Data<int, double> d2;
我们把下面一个类叫作上面一个类的特化
是int,double走下面这个最匹配的,其他的走上面那个
这个特化叫作 全特化
还有一个叫 偏特化 ,特化部分
template<class T1>
class Data<T1,int>
{
public:
Data()
{
cout << "Data<T1,int>" << endl;
}
};
还可以弄指针
template<class T1, class T2>
class Data<T1*, T2*>
{
public:
Data()
{
cout << "Data<T1*, T2*>" << endl;
}
};
注意:有最匹配的会找最匹配的
那么函数模板特化呢,这里我复用一下上一篇文章的代码
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
bool operator<(const Date& d) const
{
return (_year < d._year) ||
(_year == d._year && _month < d._month) ||
(_year == d._year && _month == d._month && _day < d._day);
}
bool operator>(const Date& d) const
{
return (_year > d._year) ||
(_year == d._year && _month > d._month) ||
(_year == d._year && _month == d._month && _day > d._day);
}
friend ostream& operator<<(ostream& _cout, const Date& d);
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
template <class T>
bool Less(T x, T y)
{
return x < y;
}
template <>
bool Less<Date*>(Date* x, Date* y)
{
return *x < *y;
};
int main()
{
Date* a1 = new Date(2025, 3, 9);
Date* a2 = new Date(2025, 3, 10);
cout << Less(a1,a2) << endl;
return 0;
}
这样就是函数的模板特化了
或者我们也不用写这个写一个函数的重载就行了
三、模板的分离编译
#include <iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right);
#include "stack.h"
template<class T>
T Add(const T& left, const T& right)
{
cout << "T Add(const T& left, const T& right)" << endl;
return left + right;
}
#include "stack.h"
template<class T>
T Add(const T& left, const T& right)
{
cout << "T Add(const T& left, const T& right)" << endl;
return left + right;
}
#include "stack.h"
int main()
{
Add(1, 2);
}
这样的声明和定义分离就会报所谓的链接错误
好让我们分析一下其中的原因
编译器运行代码一般分为四个阶段
1.预处理 像是头文件的展开/宏替换….
2.编译 检查语法生成汇编代码
3.汇编 汇编代码转成二进制机器码
4.链接
链接的时候它就需要去call这个地址,跳到这个地址,但是在.cpp文件中,只包含了stack.h的头文件,相当于只包含了声明,而没有给你定义,就是相当于你买房子给你你图纸没有给你房子,然而给了你声明就让你去其他文件去找地址
但是我们会发现Add找不到,因为stack.cpp因为Add没有实例化,没有Add的地址
我template.cpp知道要实例化成什么,但是没有定义
就相当于一种沟通不畅,template有图纸有需求就是没有房子,stack.cpp有房子有图纸就是有毛坯房就是不知道装修成什么样子装修在哪,在两个文件中,倘若我stack.cpp实例化了,那就有了具体的地址,我template手上有声明了就知道到哪里去找它了
解决方案1
显示实例化
template
int Add<int>(const int& left, const int& right);
这是一种解决方案,但是有一种弊端
Add(1.1, 2.2);
显示实例化这种方法可行但是不好用换一种类型又要写一种了;
我们连类一起看一下
这是stack.h里面的
template<class T>
class stack
{
public:
void push(const T& x);
private:
T* _a;
int _top;
int _capacity;
}
这是stack.cpp里面的
template<class T>
void stack<T>::push(const T& x)
{
cout << "void push(const T& x)" << endl;
}
stack<int> st;
st.push(1);
也是一样的结果
template
class stack<int>;
也是不好用的
解决方案二
不分离
我们直接把stack.cpp删掉,把声明和定义全都放到一起
声明和定义全都放在一起,我stack.h在编译的时候一开,直接就能实例化了
四、模板总结
优点:模板复用了代码,节省资源,更快的迭代开发
增强了代码的灵活性
缺陷
模板会导致代码膨胀问题,也会导致编译时间变长
出现模板编译错误时,错误信息非常凌乱,不易定位错误
模板进阶就到这里结束了接下来进入继承,写的不好的地方欢迎大家指出