1. 模板的概念。 我们已经学过重载(Overloading),对重载函数而言,C++的检查机制能通过函数参数的不同及所属类的不同。正确的调用重载函数。例如,为求两个数的最大值,我们定义MAX()函数需要对不同的数据类型分别定义不同重载(Overload)版本。 //函数1. int max(int x,int y); {return(x>y)?x:y ;} //函数2. float max( float x,float y){ return (x>y)? x:y ;} //函数3. double max(double x,double y) {return (c>y)? x:y ;} 但如果在主函数中,我们分别定义了 char a,b; 那么在执行max(a,b);时 程序就会出错,因为我们没有定义char类型的重载版本。 现在,我们再重新审视上述的max()函数,它们都具有同样的功能,即求两个数的最大值,能否只写一套代码解决这个问题呢?这样就会避免因重载函数定义不 全面而带来的调用错误。为解决上述问题C++引入模板机制,模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版。 2. 函数模板的写法 函数模板的一般形式如下: Template <class或者也可以用typename T> 返回类型 函数名(形参表) {//函数定义体 } 说明: template是一个声明模板的关键字,表示声明一个模板关键字class不能省略,如果类型形参多余一个 ,每个形参前都要加class <类型 形参表>可以包含基本数据类型可以包含类类型. 请看以下程序: //Test.cpp #include using std::cout; using std::endl; //声明一个函数模版,用来比较输入的两个相同数据类型的参数的大小,class也可以被typename代替, //T可以被任何字母或者数字代替。 template <class T> T min(T x,T y) { return(x void main( ) { int n1=2,n2=10; double d1=1.5,d2=5.6; cout<< "较小整数:"< cout<< "较小实数:"< system("PAUSE"); } 程序运行结果: 较小整数:2 较小实数:1.5 程序分析:main()函数中定义了两个整型变量n1 , n2 两个双精度类型变量d1 , d2然后调用min( n1, n2); 即实例化函数模板T min(T x, T y)其中T为int型,求出n1,n2中的最小值.同理调用min(d1,d2)时,求出d1,d2中的最小值. 3. 类模板的写法 定义一个类模板: Template < class或者也可以用typename T > class类名{ //类定义...... }; 说明:其中,template是声明各模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个。 例如:定义一个类模板: // ClassTemplate.h #ifndef ClassTemplate_HH #define ClassTemplate_HH template<typename T1,typename T2> class myClass{ private: T1 I; T2 J; public: myClass(T1 a, T2 b);//Constructor void show(); }; //这是构造函数 //注意这些格式 template <typename T1,typename T2> myClass<T1,T2>::myClass(T1 a,T2 b):I(a),J(b){} //这是void show(); template <typename T1,typename T2> void myClass<T1,T2>::show() { cout<<"I="<<I<<", J="<<J<<endl; } #endif // Test.cpp #include <iostream> #include "ClassTemplate.h" using std::cout; using std::endl; void main() { myClass<int,int> class1(3,5); class1.show(); myClass<int,char> class2(3,"a"); class2.show(); myClass<double,int> class3(2.9,10); class3.show(); system("PAUSE"); } 最后结果显示: I=3,J=5 I=3,J=a I=2.9,J=10 4.非类型模版参数 一般来说,非类型模板参数可以是常整数(包括枚举)或者指向外部链接对象的指针。 那么就是说,浮点数是不行的,指向内部链接对象的指针是不行的。 // sequence template #include <iostream> using namespace std; template <class T, int N> class mysequence { T memblock [N]; public: void setmember (int x, T value); T getmember (int x); }; template <class T, int N> void mysequence<T,N>::setmember (int x, T value) { memblock[x]=value; } template <class T, int N> T mysequence<T,N>::getmember (int x) { return memblock[x]; } int main () { mysequence <int,5> myints; mysequence <double,5> myfloats; myints.setmember (0,100); myfloats.setmember (3,3.1416); cout << myints.getmember(0) << ‘\n‘; cout << myfloats.getmember(3) << ‘\n‘; return 0; } It is also possible to set default values or types for class template parameters. For example, if the previous class template definition had been: template class mysequence {..}; We could create objects using the default template parameters by declaring: mysequence<> myseq; Which would be equivalent to: mysequence myseq; ========================== 几个例子 1 交换 #include <iostream> using namespace std; template<class X> void genericSwap( X &a, X &b ) { X tmp; tmp = a; a = b; b = tmp; } int main(void) { int num1 = 5; int num2 = 21; cout << "Before, num1 is " << num1 << " and num2 is " << num2 << endl; genericSwap( num1, num2 ); cout << "After, num1 is " << num1 << " and num2 is " << num2 << endl; char c1 = ‘a‘; char c2 = ‘z‘; cout << "Before, c1 is " << c1 << " and c2 is " << c2 << endl; genericSwap( c1, c2 ); cout << "After, c1 is " << c1 << " and c2 is " << c2 << endl; return( 0 ); } 2 class 模板 #include <cassert> #include <iostream> using namespace std; const unsigned int maxSize = 25; template<class T> class simpleStack { public: simpleStack(): amount(0) {} bool empty() const { return amount == 0; } bool full() const { return amount == maxSize; } unsigned int size() const { return amount; } void clear() { amount = 0; } const T& top() const; void pop(); void push( const T &x); private: unsigned int amount; T array[ maxSize ]; }; template<class T> const T& simpleStack<T>::top() const { assert( !empty() ); return array[ amount - 1 ]; } /*it‘s allowed and equal to replace class with typename*/ template<typename T> void simpleStack<T>::pop() { assert( !empty() ); --amount; } template<typename T> void simpleStack<T>::push(const T &x) { assert( !full() ); array[ amount++ ] = x; } /* main code */ int main() { simpleStack< int > aIntStack; int i = 100; while ( !aIntStack.full() ) aIntStack.push( i++ ); cout << "stack size: " << aIntStack.size() << endl; return 0; } 3 定制模板 // template specialization #include <iostream> using namespace std; // class template: template <class T> class mycontainer { T element; public: mycontainer (T arg) {element=arg;} T increase () {return ++element;} }; // class template specialization: // template <> class mycontainer <char> { ... }; template <> class mycontainer <char> { char element; public: mycontainer (char arg) {element=arg;} char uppercase () { if ((element>=‘a‘)&&(element<=‘z‘)) element+=‘A‘-‘a‘; return element; } }; int main (void) { mycontainer<int> myint (7); mycontainer<char> mychar (‘j‘); cout << myint.increase() << endl; cout << mychar.uppercase() << endl; return 0; } This is the syntax used in the class template specialization: template <> class mycontainer { ... }; First of all, notice that we precede the class template name with an empty template<> parameter list. This is to explicitly declare it as a template specialization. But more important than this prefix, is the specialization parameter after the class template name. This specialization parameter itself identifies the type for which we are going to declare a template class specialization (char). Notice the differences between the generic class template and the specialization: 1> template class mycontainer { ... }; 2> template <> class mycontainer { ... }; 参考 1 http://www.kuqin.com/language/20090405/44193.html 2 http://www.cplusplus.com/doc/tutorial/templates/ 3 www.cppreference.com
时间: 2024-10-22 16:08:39