在学习stl之前,我们就已经略微讲解了一些模板的知识,而现在,我们来进一步了解一下模板的相关知识
目录
一. 非类型模板参数
模板参数分类类型形参与非类型形参。
在我们之前所使用到的模板参数都是类型形参,
而非类型形参就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。而非类型形参的的参数类型只能是整形
例如我们可以通过非类型模板参数来实现一个静态的数组
template<class T, size_t N>
class array
{
public:
private:
T _array[N];
};
而类型模板参数可以有缺省值,同样非类型模板参数也可以有缺省值
namespace szt
{
template<class T, size_t N = 10>
class array
{
public:
array()
{
cout << sizeof(_array) / sizeof(T) << endl;
}
private:
T _array[N];
};
void test1()
{
array<int> arr;
}
}
int main()
{
szt::test1();
}
二. 模板的特化
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理
全特化
字面意思就是将所有的模板参数进行特化
template<class T1, class T2>
class Data
{
public:
Data() { cout << "Data<T1, T2>" << endl; }
};
template<>
class Data<int, char>
{
public:
Data() { cout << "Data<int, char>" << endl; }
};
int main()
{
Data<int, int> d1;
Data<int, char> d2;
}
而在之前的模拟优先级队列中, 我们说过针对于自定义类型,需要自己写一个新的仿函数来完成自定义类型的比较,而我们也可以使用特化来实现比较
偏特化
将模板参数类表中的一部分参数特化。
首先,就是可以特化一部分的模板参数
template<class T1, class T2>
class Data
{
public:
Data() { cout << "Data<T1, T2>" << endl; }
};
template<class T>
class Data<T, int>
{
public:
Data() { cout << "Data<T, int>" << endl; }
};
int main()
{
Data<int, int> d1;
Data<int, char> d2;
}
其次呢,也可以针对模板参数进行更进一步的条件限制
例如限制为指针、引用等等
template<class T1, class T2>
class Data
{
public:
Data() { cout << "Data<T1, T2>" << endl; }
};
template<class T1,class T2>
class Data<T1*, T2*>
{
public:
Data() { cout << "Data<T*, T*>" << endl; }
};
int main()
{
Data<int, int> d1;
Data<int*, char*> d2;
}
三. 模板分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
在我们之前所写的一些代码中,若是量较大,往往会将函数的声明放在头文件中,并在源文件中进行定义,这就是一种分离编译模式
那么模板能不能也采用我们在函数中所使用的方法呢?
在讨论这个问题之前,我们先了解一下程序的运行过程
主要分为四个阶段:预处理、编译、汇编、链接
首先预处理所处理的是头文件的展开、条件编译、宏定义、去除注释等问题,并将对应的源文件转换为.i类型的文件
而编译我们就比较熟悉了,是用来检查语法错误的,没有错误就将代码转为汇编语言,转为.s文件
汇编就是将汇编语言转换为二进制机器码,即.o文件
而链接是将多个源文件链接在一起,一个源文件若是想要使用所包含的头文件中声明的函数或类,就需要通过链接来使用
而若是将模板的声明与定义分离,往往会提示链接上出错,这是因为在编译的过程中,由于模板是在一个单独的源文件中进行定义,因此没有进行实例化,所以无法转为汇编语言,因此在使用时无法链接到对应的函数或者类。
而为了解决这个问题,我们有两种方法,其中一种就是在模板定义的位置显式实例化,而若是使用这种方法,需要我们每一种类型都需要进行一次显示实例化,不方便
而另一种解决方法就是将声明和定义都放在头文件中,这样,在编译的过程中,由于定义与使用在一个文件中(头文件的展开),所以就不需要进行链接,同时也可以进行实例化。
四. 总结
优点
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性
缺陷
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误