深入理解C++模板
一.函数模板1.模板的意义假如我们要写一系列函数用于比较两个数的大小但是两个数的类型有很多种可以是整形浮点型可以是字符类型那么常规的方法是为每一个类型都写一个函数但是我们会发现这很麻烦并且进行比较的函数都十分的相似只有参数列表不同。为了在这样的情况中简化程序我们可以使用函数的模板所谓函数的模板实际上就是对函数的形参类型进行了参数化。2.函数模板的实现如图我们实现支持多种类型的比较函数这里的typename也可以用class替换。当我们要传入两个整形进行比较时可以通过compareint(10,20)这里的compare实际上上一个模板模板名加参数列表后才称为函数来调用。我们称这里的compareint(10,20)为函数的调用点在函数的调用点编译器用用户指定的类型从原模板实例化一份函数代码出来也就是说在编译器的角度仍然是编译多个模板函数为compare的重载函数编译的函数数量并未减少。但同时这里也可以直接使用compare(10,20)来调用这是由于函数模板存在模板实参推演可以通过用户传入的参数推导出来模板的类型。但是如果我们这里compare(10,20.5)那么这里就无法正确推演出T的具体类型为什么就会出现错误但是这时如果我们使用compareint(10,20.5)这里就不会出现错误会将double类型的数据强转为int类型。由于函数模板不确定T的类型所以函数模板不进行编译编译的是模板函数。我们再来看假如我们要比较两个字符串使用compare(aaa,bbb)从结果上看确实能够进行比较但是模板识别的T为const char* 这里比较使用实际上是两个字符串的地址大小明显与我们的期望不符比较字符串我们应该使用strcmp等字符串比较函数不能直接。面对这种情况我们就要对字符串单独采用一个模板实现即模板的特例化专用化如图如果我们再定义一个非模板函数普通函数也进行字符串的比较那么编译器会优先调用非模板函数更为高效。由于函数模板不进行编译所以在多文件工程中不能在一个文件中声明使用模板在另一个文件中实现模板但对于特例化的模板可以正常使用因为模板的特例化实际上生成了一个模板函数所以一般模板都是定义在头文件中在源文件中直接#include##include导入的头文件直接在预编译阶段展开。当然这种情况也是有办法解决的不过并不推荐我们可以在函数模板定义后直接告诉编译器进行指定类型的模板实例化如templete bool compareint(int,int)。模板非类型参数我们可以在模板中定义非类型参数不过只能使用整数类型地址引用也可并且为常量。二.类模板如下我们利用模板实现一个顺序栈templatetypename T class stack { private: vectorTmpstack; public: stack() { } public: //入栈操作 void push(T val) { mpstack.push_back(val); } //出栈操作 void pop() { if (mpstack.empty()) { throw mpstack is empty; } mpstack.pop_back(); } //获取栈顶操作 int top()const { if (mpstack.empty()) { throw mpstack is empty; } return mpstack.back(); } //判断栈是否为空 bool empty()const { return mpstack.empty(); } };此时stack已经不在是一个类而是一个类模板此时的类名称为模板名称类型参数列表。同时对于所有所有到类的地方除了构造与析构函数不用加其他地方最好都加上虽然不加编译器会自动帮我们加。类模板会进行选择实例化如果我们利用模板类定义了一个对象编译器并不会将类中所有的成员方法都进行实例化而是看我们调用了那些。