模板
模板主要是提供一种处理方式,相当于加强版的函数处理,里面的参数类型,处理方式都能够被模板化,传统的函数处理是不能够针对多种处理方式的,所以会出现函数的重载问题。
模板主要包括两个方面:
- 针对函数的模板,格式:template 函数返回值 函数名称 (函数参数){函数处理};例如template void swap(T& a, T& b){},其中的class能够变成typename
- 针对类的模板,格式template class classname{ … };例子:template class A{public: T a; T b; T hy(T c, T &d);};当函数在外部声明的是姑姑也template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体},所以为template T A::hy(T c, T &d);
针对函数的模板
格式:
template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表)
{
函数体
}
例子:
template <class T> T add (T a,T b){
return a+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 T1,class T2...> class A
{
...
T1 add(T1 t1,T2 t2);
}
template <class T1,class T2> T1 A<T1,T2>::add(T1 t1,T2 t2)
{
return (T1)(t1+t2);
}
调用:(假如有两个参数)
那么
A<int,double> m
m就是变量,其中里面的T1对应int类性数据,T2对应double类型数据
类模板对象的创建:比如一个模板类A,则使用类模板创建对象的方法为A m;在类A后面跟上一个<>尖括号并在里面填上相应的类型,这样的话类A中凡是用到模板形参的地方都会被int 所代替。当类模板有两个模板形参时创建对象的方法为A<int, double> m;
类型之间用逗号隔开。
外部函数声明方式:
template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体}
比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:
template<class T1,class T2> void A<T1,T2>::h(){}
注意模板的声明只能够在全局变量中申明,不能够在局部声明
例子:
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems; // 元素
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem)
{
// 追加传入元素的副本
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 删除最后一个元素
elems.pop_back();
}
template <class T>
T Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一个元素的副本
return elems.back();
}
int main()
{
try {
Stack<int> intStack; // int 类型的栈
Stack<string> stringStack; // string 类型的栈
// 操作 int 类型的栈
intStack.push(7);
cout << intStack.top() <<endl;
// 操作 string 类型的栈
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
运行结果
7
hello
Exception: Stack<>::pop(): empty stack
typename 另外一个作用为:使用嵌套依赖类型(nested depended name),如下所示:
class MyArray
{
public:
typedef int LengthType;
.....
}
template<class T>
void MyMethod( T myarr )
{
typedef typename T::LengthType LengthType;
LengthType length = myarr.GetLength;
}
这个时候 typename 的作用就是告诉 c++ 编译器,typename 后面的字符串为一个类型名称,而不是成员函数或者成员变量,这个时候如果前面没有
typename,编译器没有任何办法知道 T::LengthType 是一个类型还是一个成员名称(静态数据成员或者静态函数),所以编译不能够通过。