本文学习内容参考:http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html
C++模板
1.模板分为函数模板和类模板两种类型
函数模板针对参数类型不同的函数;
类模板针对数据成员和成员函数类型不同的类;
使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板;
2.函数模板的通用格式如下:
template <class 形参名, class 形参名,......> 返回类型 函数名(参数列表) { 函数体 }
其中template 和 class 是关键字,这里的class也可以用 typename 关键字来代替。<>中的参数叫做模板形参,模板形参不能为空。一旦声明了模板函数,就可以用模板函数的形参名声明函数中的成员,即在该函数中使用内置类型的地方都可以使用模板形参名。模板函数在调用时,通过模板函数的实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。下面以swap的模板函数为例:
template <class T> void swap(T& a, T& b){};
当调用这样的模板函数时,这里的类型 T 就会被调用时的类型所替代。比如swap(a , b),当 a 和 b 是 int 时,模板函数swap中的形参 T 就会被 int 所替代,模板函数就变为了swap(int& a, int& b)。而当swap(a , b)中的 a 和 b 为 double 类型时,模板函数就相应地变为了swap(double& a, double& b)。这样也就实现了代码与类型无关。
Demo: 求两个数的最大值的模板函数
#include<iostream> using namespace std; template <typename T> const T& max(const T& a, const T& b) { return a > b ? a : b; } int main() { cout << max(2.1, 3.3) << endl; //模板实参被隐式推演为double类型 cout << max<double>(2.1, 3.3) << endl; //显示指定实参类型为double cout << max<int>(2.1, 3.3) << endl; //显示指定实参类型为int system("pause"); return 0; }
result:
3.类模板的通用格式如下:
template <class 形参名, class 形参名......> class 类名 { .......... };
3.1类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。
template <class T> class Demo { T a; public: Demo(T first); T GetValue(); };
3.2类模板的成员函数在类外面实现的定义方式如下(需要在每个方法的实现前面添加 template<class T>):
template <class T> Demo<T>::Demo(T first){ a=first; } template <class T> T Demo<T>::GetValue(){ return a; }
4.模板特殊化(Template Specialization)(此处学习内容参考http://blog.csdn.net/fingding/article/details/32079289)
4.1模板的特殊化是当模板中的pattern有确定的类型时,模板有一个具体的实现。例如假设我们的类模板pair 包含一个取模计算(module operation)的函数,而我们希望这个函数只有当对象中存储的数据为整型(int)的时候才能工作,其他时候,我们需要这个函数总是返回0。这可以通过下面的代码来实现:
#include<iostream> using namespace std; template<class T> class Pair { T value1; T value2; public: Pair(T first, T second) { value1 = first; value2 = second; } T Module() { return 0; } }; template<> class Pair<int> { int value1; int value2; public: Pair(int first, int second) { value1 = first; value2 = second; } int Module(); };
//template<> //此处正常情况下需要,但是在VS2015中编译的结果如下result11 int Pair<int>::Module() { return value1%value2; } int main() { Pair<int> p1(8, 3); Pair<float> p2(5.5, 2.3); cout << p1.Module() << endl; cout << p2.Module() << endl; system("pause"); return 0; }
result11:
1>------ 已启动生成: 项目: Template specialization, 配置: Debug Win32 ------ 1> TemplateMain.cpp 1>c:usersadministratordocumentsvisual studio 2015projectssuanfashiyan1 est1--sequence emplate specialization emplatemain.cpp(33): error C2910: “Pair<int>::Module”: 无法显式专用化 1>c:usersadministratordocumentsvisual studio 2015projectssuanfashiyan1 est1--sequence emplate specialization emplatemain.cpp(38): warning C4305: “参数”: 从“double”到“float”截断 ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
以上的错误:error C2910 提示无法显示专用化,查询C2910错误代码,解决方案:去掉函数Module()实现时前面的 template<>即可;
可以学习到很多关于生成与错误处理知识的链接:https://msdn.microsoft.com/zh-cn/library/z7kx322x.aspx
修改后的运行结果如下:
5.非类型形参示例(参考:http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html)
该示例实现的是让用户亲自指定栈的大小,并实现栈的相关操作
TemplateDemo.h
#pragma once template <class T, int MAXSIZE>class Stack { //MAXSIZE由用户创建对象时自行设定 private: T elements[MAXSIZE]; //包含栈中元素的数组 int numElements; //当前栈中元素的个数 public: Stack(); //构造函数 void push(T const&); //压入元素 void pop(); //弹出元素 T top() const; //返回栈顶元素 bool Empty() const { //判断栈是否为空 return numElements == 0; } bool Full() const { //判断栈栈是否已满 return numElements == MAXSIZE; } }; template <class T,int MAXSIZE> Stack<T, MAXSIZE>::Stack() :numElements(0) { //初始时栈不含任何元素 } template <class T,int MAXSIZE> void Stack<T, MAXSIZE>::push(T const& elem) { if (numElements == MAXSIZE) { throw std::out_of_range("Stack<>::push():Stack if fulll"); } elements[numElements] = elem; //将新元素elem添加到栈中 ++numElements; //当前栈中元素个数加1 } template <class T,int MAXSIZE> void Stack<T, MAXSIZE>::pop() { if (numElements <= 0) { throw std::out_of_range("Stack<>::pop(),Stack is Empty"); } --numElements; //减少元素个数 } template <class T,int MAXSIZE> T Stack<T, MAXSIZE>::top() const{ if (numElements <= 0) { throw std::out_of_range("Stack<>::pop(),Stack is Empty"); } return elements[numElements - 1]; //返回最后(栈顶)一个元素 }
TemplateDemo.cpp
#include<iostream> #include<string> #include"TemplateDemo.h" using namespace std; int main() { try { Stack<int, 20> int20Stack; //可以存储20个int类型数据的栈 Stack<int, 40> int40Stack; //可以存储40个int类型数据的栈 Stack<string, 40> string40Stack; //可以存储40个string类型数据的栈 //使用存储20个int类型数据的栈 int20Stack.push(7); cout << int20Stack.top() << endl; int20Stack.pop(); //使用可存储40个string类型数据的栈 string40Stack.push("hello"); cout << string40Stack.top() << endl; string40Stack.pop(); string40Stack.pop(); system("pause"); return 0; } catch (const std::exception& ex) { cerr << "Exception: " << ex.what() << endl; system("pause"); return EXIT_FAILURE; //退出程序且有error标记 } }
result: