定义理解
1、模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
2、模板是c++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。因此可以说,模板是一种对类型进行参数化的工具。
3、模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector <int> 或 vector <string>。
4、使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
模板分类以及定义
主要分为两大类:函数模板和类模板。
函数模板针对仅参数类型不同的函数;
类模板针对仅数据成员和成员函数类型不同的类。
官方定义通式
关于函数模板和类模板的定义,官方给出的定义分别如下:
(1) 模板函数定义的一般形式如下所示:
template <typename type> ret-type func-name(parameter list)
{
// 函数的主体
}
在这里,type 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用。
(2) 泛型类声明的一般形式如下所示:
template <class type> class class-name {
.
.
.
}
在这里,type 是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。
理解定义通式
在实际编程中,template<class T> 和 template<typename T> 都可以用来定义函数模板和类模板,在使用上,他们俩没有本质的区别。即,其中template和class是关见字,class可以用typename 关见字代替,在这里typename 和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。
即,函数模板和类模板的通式也可以写为如下所示:
函数模板:
template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表)
{
函数体
}
例:
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(c,d)其中c和d是double类型时,模板函数会被替换为swap(double &a, double &b),
这样就实现了函数的实现与类型无关的代码。
类模板:
template<class 形参名,class 形参名,…> class 类名
{ ... };
类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,
一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,
即可以在类中使用内置类型的地方都可以使用模板形参名来声明。比如
例:
template<class T> class A{public: T a; T b; T hy(T c, T &d);};
理解:
1、在类A中声明了两个类型为T的成员变量a和b,还声明了一个返回类型为T带两个参数类型为T的函数hy。
2、类模板对象的创建:比如一个模板类A,则使用类模板创建对象的方法为A<int> m;
在类A后面跟上一个<>尖括号并在里面填上相应的类型,这样的话类A中凡是用到模板形参
的地方都会被int 所代替。当类模板有两个模板形参时创建对象的方法为A<int, double> m;
类型之间用逗号隔开。
模板类外定义模板类成员函数
template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体},
比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:
template<class T1,class T2> void A<T1,T2>::h(){}。
注意点
1、模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
2、对于函数模板而言不存在 h(int,int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)。
3、对于类模板,模板形参的类型必须在类名后的尖括号中明确指定。比如A<2> m;用这种方法把模板形参设置为int是错误的(编译错误:error C2079: 'a' uses undefined class 'A<int>'),类模板形参不存在实参推演的问题。也就是说不能把整型值2推演为int 型传递给模板形参。要把类模板形参调置为int 型必须这样指定A<int> m。
4、当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。
范例
以下范例包含函数模板和类模板的使用。
#include <iostream>
using namespace std;
//mySwap函数模板声明
template<typename T> void mySwap(T &t1, T &t2);
//mySwap函数模板定义
template<typename T> void mySwap(T &t1, T &t2)
{
T tmpT;
cout << "t1:" << t1 << ", t2:" << t2 << endl;
tmpT = t1;
t1 = t2;
t2 = tmpT;
}
//类模板声明
template <class T> class myStack {
public:
myStack();
~myStack();
void push(T t);
T pop();
bool isEmpty();
private:
T *m_pT;
int m_maxSize;
int m_size;
};
//类实现,即类模板外定义成员函数
template <class T> myStack<T> :: myStack() {
m_maxSize = 100;
m_size = 0;
m_pT = new T[m_maxSize];
}
template <class T> myStack<T> :: ~myStack() {
delete [] m_pT;
}
template <class T> void myStack<T> :: push(T t) {
m_size++;
m_pT[m_size - 1] = t;
}
template <class T> T myStack<T> :: pop() {
T t = m_pT[m_size - 1];
m_size--;
return t;
}
template <class T> bool myStack<T> :: isEmpty() {
return m_size == 0;
}
int main(int argc, char* argv[])
{
//使用mySwap函数模板
//int类型
int num1 = 1;
int num2 = 2;
//char类型
char *name1 = "malixing";
char *name2 = "wangjing";
//string类型
string str1 = "hello";
string str2 = "world";
mySwap<int>(num1, num2);
cout << "num1: " << num1 << ", num2: " << num2 << endl;
mySwap<char*>(name1, name2);
cout << "name1:" << name1 << ", name2:" << name2 << endl;
mySwap<string>(str1, str2);
cout << "str1:" << str1 << ", str2:" << str2 << endl;
//使用myStack类模板
//int类型
myStack<int> intStack;
intStack.push(1);
intStack.push(2);
intStack.push(3);
//string类型
myStack<string> strStack;
strStack.push("hello");
strStack.push("world");
cout << "intStack: " << endl;
while (!intStack.isEmpty()) {
cout << "num:" << intStack.pop() << " " ;
}
cout << endl;
cout << "strStack: " << endl;
while (!strStack.isEmpty()) {
cout << "str:" << strStack.pop() << " ";
}
cout << endl;
return 0;
}