标签:
模板是 C++ 语言中重要的概念。它提供了一种通用的方法来开发重用的代码,即以创建参数化的 C++ 类型。模板分为两种类型:函数模板和类模板。函数模板的用法同 C++ 预处理器的用法有一定的类似之处,它们都提供编译代码过程中的文本替换功能,但函数模板还能对类型进行一定的保护。使用类模板可以编写通用的、类型安全的类。
#include <iostream> using namespace std; /* int sum(int data[],int nsize) { int sum=0; for(int i=0;i<nsize;i++) { sum+=data[i]; } return sum; } */ template <class T> T sum(T data[],int nsize) { T sum=0; for(int i=0;i<nsize;i++) { sum+=data[i]; } return sum; } int main() { int data1[]={1,2,3,4,5}; float data2[]={1.1,2.2,3.3,4.4,5.5}; double data3[]={1.1,2.2,3.3,4.4,5.5}; cout<<sum(data1,5)<<endl<<sum(data2,5)<<endl<<sum(data3,5); return 0; }
注释中的代码为int型数组相加的函数,将其改为下方模板函数时,只需要将数据类型抽象出来,使用 template<class name> 代替原先的变量,即可实现模板功能,函数中的类型相应地也要改为 name 。
该函数已经能够实现对不同的基本数据类型进行求和,若进一步思考,希望用该函数能够实现对链表、集合等元素的求和,这即是 STL 的思维方式。
另外,用于模板时,class 关键词可以用 typename 代替,即 template<typename name> 。
#include <iostream> using namespace std; template <class T> //一个简单的模板类,只有构造函数、析构函数、添加元素函数,但已经能够说明模板类的特征了。 class MyArray { private: int m_nTotalSize; //数组总长度 int m_nValidSize; //数组有效长度,即当前元素数 T * m_pData; //数据指针 public: //构造函数 MyArray(int nSize=2) //假设默认数组长度为2 { m_pData=new T[nSize]; m_nTotalSize=nSize; m_nValidSize=0; } //获取数组有效长度 int GetValidSize() { return m_nValidSize; } //获取数组总长度 int GetTotalSize() { return m_nTotalSize; } //返回某一位置元素 T Get(int pos) { return m_pData[pos]; } //添加一个元素 void Add(T value) { if(m_nValidSize<m_nTotalSize) //若数组未满 { m_pData[m_nValidSize]=value; m_nValidSize++; } else //数组满时动态增加数组大小 { T * pOldData=m_pData; //保存当前数据指针 m_pData=new T[m_nTotalSize*2]; //原先数组空间大小扩大两倍 for(int i=0;i<m_nTotalSize;i++) //拷贝原先数据 { m_pData[i]=pOldData[i]; } m_nTotalSize*=2; //当前数组总长度更新 delete pOldData; //释放旧数组占用的内存 m_pData[m_nValidSize]=value; //添加新元素 m_nValidSize++; //更新数组有效程度 } } //析构函数 virtual ~MyArray() { if(m_pData!=NULL) { delete []m_pData; m_pData=NULL; } } }; int main() { MyArray<int> array1; cout<<"数组总长度:"<<array1.GetTotalSize()<<" 数组有效长度:"<<array1.GetValidSize()<<endl; array1.Add(1); array1.Add(2); array1.Add(3); cout<<"数组总长度:"<<array1.GetTotalSize()<<" 数组有效长度:"<<array1.GetValidSize()<<endl; cout<<array1.Get(0)<<" "<<array1.Get(1)<<" "<<array1.Get(2)<<endl; MyArray<double> array2; cout<<"数组总长度:"<<array2.GetTotalSize()<<" 数组有效长度:"<<array2.GetValidSize()<<endl; array2.Add(1.1); array2.Add(2.2); array2.Add(3.3); cout<<"数组总长度:"<<array2.GetTotalSize()<<" 数组有效长度:"<<array2.GetValidSize()<<endl; cout<<array2.Get(0)<<" "<<array2.Get(1)<<" "<<array2.Get(2)<<endl; return 0; }该模板类模拟了一个简化的类似 vector 的动态数组,并实现了添加元素功能。可以看到该数组类默认数组大小是2,当插入3个元素之后,数组大小动态增加为原来的两倍,这也是 vector 中的实现方法。虽然实现了数组动态大小,但是需要将数组的原先数据做一份拷贝,因此此时会有效率上的损失。
#include <iostream> using namespace std; class IntArray { private: int a[10]; public: IntArray() { for(int i=0;i<10;i++) { a[i]=i+1; } } //该函数输出所有元素的和与times的乘积 int Calculate(int times) { int sum=0; for(int i=0;i<10;i++) { sum+=a[i]; } return sum * times; } }; class FloatArray { private: float a[10]; public: FloatArray() { for(int i=0;i<10;i++) { a[i]=i+1; } } //该函数输出所有元素的和与times的乘积 float Calculate(float times) { int sum=0; for(int i=0;i<10;i++) { sum+=a[i]; } return sum * times; } }; int main() { IntArray a; FloatArray f; cout<<"整形数组和的 2 倍是:"<<a.Calculate(2)<<endl; cout<<"浮点型数组和的 2.2倍是: "<<f.Calculate(2.1)<<endl; }可以发现,两个数组类的 Calculate 函数出了参数类型、返回值类型不同外,其余都一样,那么能否通过一个类的接口函数来完成上述功能呢?可以,当然要用到模板。这里要增加一个类 Array 。
template<class T> class Array { public: float Calculate(T &t,float times) { return t.Calculate(times); } };并且主函数需要改变:
int main() { IntArray a; FloatArray f; Array<IntArray> A1; Array<FloatArray> A2; cout<<"整形数组和的 2 倍是:"<<A1.Calculate(a,2)<<endl; cout<<"浮点型数组和的 2.2倍是: "<<A2.Calculate(f,2.1)<<endl; }这里使用 Array 接口函数实现对整形数组和浮点型数组类的操作。但是仔细分析一下就能发现,细节上还是有问题的。比如在 IntArray 中的 Calculate() 函数的参数和返回值都是 int 类型,FloatArray 中的 Calculate() 函数的参数和返回值都是 float 类型,而模板类 Array 中将 Calculate() 函数的参数和返回值都固定为了 float 类型,虽然从程序的结果来看似乎是正确的,但是并不够严密,当程序复杂点时极有可能出错,那么如何解决输出、输出参数类型的不同呢?traits 技术就是很好的解决方法,步骤与如下所示。
template<>
class NumTraits<FloatArray>
{
public:
typedef int inputType;
typedef int resultType;
};
可以看出相应模板特化类中只是用了 typedef 重定义函数,将 IntArray 和 FloatArray 两个数组类中 Calculate() 函数的参数类型、返回值类型重新定义成 inputType 和 resultType,为编写模板类共同的调用接口做准备。
#include <iostream> using namespace std; class IntArray { private: int a[10]; public: IntArray() { for(int i=0;i<10;i++) { a[i]=i+1; } } //该函数输出所有元素的和与times的乘积 int Calculate(int times) { int sum=0; for(int i=0;i<10;i++) { sum+=a[i]; } return sum * times; } }; class FloatArray { private: float a[10]; public: FloatArray() { for(int i=0;i<10;i++) { a[i]=i+1; } } //该函数输出所有元素的和与times的乘积 float Calculate(float times) { int sum=0; for(int i=0;i<10;i++) { sum+=a[i]; } return sum * times; } }; //基本模板类 template<class T> class NumTraits { }; //模板特化 template<> class NumTraits<IntArray> { public: typedef int inputType; typedef int resultType; }; //模板特化 template<> class NumTraits<FloatArray> { public: typedef float inputType; typedef float resultType; }; //统一模板调用类编写 template<class T> class Array { public: typedef typename NumTraits<T>::resultType result; typedef typename NumTraits<T>::inputType input; result Calculate(T &t,input times) { return t.Calculate(times); } }; int main() { IntArray a; FloatArray f; Array<IntArray> A1; Array<FloatArray> A2; cout<<"整形数组和的 2 倍是:"<<A1.Calculate(a,2)<<endl; cout<<"浮点型数组和的 2.2倍是: "<<A2.Calculate(f,2.1)<<endl; return 0; }
#include <iostream> using namespace std; template <class U,class V> bool Grater(U const &u,V const &v) { return u>v; } int main() { cout<<Grater(2,1)<<endl; cout<<Grater(2.2,1.1)<<endl; cout<<Grater('a',10)<<endl; return 0; }该模板函数在比较基本类型时完全正确,但是当 U 或者 V 中有一个表示类时,无法比较,此时就需要重载操作符。
class Student { private: char name[20]; int grade; public: Student(char name[],int grade) { strcpy(this->name,name); this->grade=grade; } bool operator>(const int &value)const { return this->grade>value; } };需要比较他的成绩是否大于99分,使用 Grater(s,99) 时,需要重载 Student 类的 ">" 操作符,代码如下所示:
#include <iostream> #include <string.h> using namespace std; class Student { private: char name[20]; int grade; public: Student(char name[],int grade) { strcpy(this->name,name); this->grade=grade; } bool operator>(const int &value)const { return this->grade>value; } }; template <class U,class V> bool Grater(U const &u,V const &v) { return u>v; } int main() { Student s("Raito",100); cout<<Grater(s,99)<<endl; return 0; }由于 STL 中有大量的模板函数,因此很多时候都要重载与之对应的操作符。模板函数相当于已经编写好的应用框架,操作符重载相当于调用的接口。
标签:
原文地址:http://blog.csdn.net/raito__/article/details/51475839