C++模版:
模版时C++支持多参数多态的工具,使用模版可以为用户为类或函数声明一般模式,使得类的数据成员,或者成员函数的参数,返回值取得任意类型。
模版是一种对类型进行参数化的工具;
通常有两种形式:函数模版和类模版;
函数模板针对仅参数类型不同的函数;
类模板针对仅数据成员和成员函数类型不同的类。
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
一、函数模板通式
1、函数模板的格式:
template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表)
{
函数体
}
其中template和class是关见字,class可以用typename 关见字代替,在这里typename 和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。比如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(c,d)其中c和d是double类型时,模板函数会被替换为swap(double &a, double &b),这样就实现了函数的实现与类型无关的代码。
2、注意:对于函数模板而言不存在 h(int,int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)。
函数模板的示例演示将在下文中涉及!
二、类模板通式
1、类模板的格式为:
template<class 形参名,class 形参名,…> class 类名
{ ... };
类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。比如
template<class T> class A{public: T a; T b; T hy(T c, T &d);};
在类A中声明了两个类型为T的成员变量a和b,还声明了一个返回类型为T带两个参数类型为T的函数hy。
2、类模板对象的创建:比如一个模板类A,则使用类模板创建对象的方法为A<int> m;在类A后面跟上一个<>尖括号并在里面填上相应的类型,这样的话类A中凡是用到模板形参的地方都会被int 所代替。当类模板有两个模板形参时创建对象的方法为A<int, double> m;类型之间用逗号隔开。
3、对于类模板,模板形参的类型必须在类名后的尖括号中明确指定。比如A<2> m;用这种方法把模板形参设置为int是错误的(编译错误:error C2079: 'a' uses undefined class 'A<int>'),类模板形参不存在实参推演的问题。也就是说不能把整型值2推演为int 型传递给模板形参。要把类模板形参调置为int 型必须这样指定A<int> m。
4、在类模板外部定义成员函数的方法为:
template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体},
比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:
template<class T1,class T2> void A<T1,T2>::h(){}。
注意:当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。
5、再次提醒注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
#include <iostream> //C++类型模版参数(类型形参,非类型形参,模版形参) //--------------------------------------------- //----------类型形参---------------- //------------------------------------------- template<typename T1,typename T2> class A{ public: T1 show(T1,T2); A(); }; template<typename T1,typename T2> T1 A<T1,T2>::show(T1 a, T2 b) {return a+b;} template <typename T1,typename T2> A<T1,T2>::A(){} int main() { A<int,int> a; std::cout<<a.show(3,4)<<std::endl; return 0; }
1 #include <iostream> 2 3 4 //C++类型模版参数(类型形参,非类型形参,模版形参) 5 //--------------------------------------------- 6 //-----------非类型形参--------------- 7 //------------------------------------------- 8 template<typename T1,int a> 9 class A 10 { 11 public: 12 A(); 13 T1 Some_Show(T1); 14 }; 15 16 17 template<typename T1,int a> A<T1,a>::A(){}; 18 template<typename T1,int a> T1 A<T1,a>::Some_Show(T1 c){return c+a;} 19 20 int main() 21 { 22 A<double,4> a; 23 std::cout<<a.Some_Show(3.5)<<std::endl; 24 return 0; 25 }
1 #ifndef TEMPLATE_DEMO_HXX 2 #define TEMPLATE_DEMO_HXX 3 4 template<class T,int MAXSIZE> class Stack{//MAXSIZE由用户创建对象时自行设置 5 private: 6 T elems[MAXSIZE]; // 包含元素的数组 7 int numElems; // 元素的当前总个数 8 public: 9 Stack(); //构造函数 10 void push(T const&); //压入元素 11 void pop(); //弹出元素 12 T top() const; //返回栈顶元素 13 bool empty() const{ // 返回栈是否为空 14 return numElems == 0; 15 } 16 bool full() const{ // 返回栈是否已满 17 return numElems == MAXSIZE; 18 } 19 }; 20 21 template <class T,int MAXSIZE> 22 Stack<T,MAXSIZE>::Stack():numElems(0){ // 初始时栈不含元素 23 // 不做任何事情 24 } 25 26 template <class T,int MAXSIZE> 27 void Stack<T, MAXSIZE>::push(T const& elem){ 28 if(numElems == MAXSIZE){ 29 throw std::out_of_range("Stack<>::push(): stack is full"); 30 } 31 elems[numElems] = elem; // 附加元素 32 ++numElems; // 增加元素的个数 33 } 34 35 template<class T,int MAXSIZE> 36 void Stack<T,MAXSIZE>::pop(){ 37 if (numElems <= 0) { 38 throw std::out_of_range("Stack<>::pop(): empty stack"); 39 } 40 --numElems; // 减少元素的个数 41 } 42 43 template <class T,int MAXSIZE> 44 T Stack<T,MAXSIZE>::top()const{ 45 if (numElems <= 0) { 46 throw std::out_of_range("Stack<>::top(): empty stack"); 47 } 48 return elems[numElems-1]; // 返回最后一个元素 49 } 50 51 #endif
1 #include<iostream.h> 2 #include <iostream> 3 #include <string> 4 #include <cstdlib> 5 #include "TemplateDemo.h" 6 7 int main(){ 8 try { 9 Stack<int,20> int20Stack; // 可以存储20个int元素的栈 10 Stack<int,40> int40Stack; // 可以存储40个int元素的栈 11 Stack<std::string,40> stringStack; // 可存储40个string元素的栈 12 13 // 使用可存储20个int元素的栈 14 int20Stack.push(7); 15 std::cout << int20Stack.top() << std::endl; //7 16 int20Stack.pop(); 17 18 // 使用可存储40个string的栈 19 stringStack.push("hello"); 20 std::cout << stringStack.top() << std::endl; //hello 21 stringStack.pop(); 22 stringStack.pop(); //Exception: Stack<>::pop<>: empty stack 23 return 0; 24 } 25 catch (std::exception const& ex) { 26 std::cerr << "Exception: " << ex.what() << std::endl; 27 return EXIT_FAILURE; // 退出程序且有ERROR标记 28 } 29 }
类模版参数<T1&>与类模版参数<T1 const&>的区别,传入一个是变量引用,一个是常量直接引用.
四、类模板的默认模板类型形参
1、可以为类模板的类型形参提供默认值,但不能为函数模板的类型形参提供默认值。函数模板和类模板都可以为模板的非类型形参提供默认值。
2、类模板的类型形参默认值形式为:template<class T1, class T2=int> class A{};为第二个模板类型形参T2提供int型的默认值。
3、类模板类型形参默认值和函数的默认参数一样,如果有多个类型形参则从第一个形参设定了默认值之后的所有模板形参都要设定默认值,比如template<class T1=int, class T2>class A{};就是错误的,因为T1给出了默认值,而T2没有设定。
4、在类模板的外部定义类中的成员时template 后的形参表应省略默认的形参类型。比如template<class T1, class T2=int> class A{public: void h();}; 定义方法为template<class T1,class T2> void A<T1,T2>::h(){}。
定义类模板类型形参:
演示实例1:
TemplateDemo.h
1 #ifndef TEMPLATE_DEMO_HXX 2 #define TEMPLATE_DEMO_HXX 3 4 template<class T> class A{ 5 public: 6 T g(T a,T b); 7 A(); 8 }; 9 10 #endif
1 #include<iostream.h> 2 #include "TemplateDemo.h" 3 4 template<class T> A<T>::A(){} 5 6 template<class T> T A<T>::g(T a,T b){ 7 return a+b; 8 } 9 10 void main(){ 11 A<int> a; 12 cout<<a.g(2,3)<<endl; 13 }
1 #ifndef TEMPLATE_DEMO_03 2 #define TEMPLATE_DEMO_03 3 //定义带默认类型形参的类模板。这里把T2默认设置为int型。 4 template<class T1,class T2=int> class CeilDemo{ 5 public: 6 int ceil(T1,T2); 7 }; 8 //在类模板的外部定义类中的成员时template 后的形参表应省略默认的形参类型。 9 template<class T1,class T2> 10 int CeilDemo<T1,T2>::ceil(T1 a,T2 b){ 11 return a>>b; 12 } 13 14 #endif
1 #include<iostream.h> 2 #include "TemplateDemo03.h" 3 4 void main(){ 5 CeilDemo<int> cd; 6 cout<<cd.ceil(8,2)<<endl; 7 }
从结果我们可以看出,和上例是一样的错误。从实例中我们可以总结如下:类模板如果有多个类型形参,如果使用类型形参默认值则尽量放在参数列表的末尾,而且默认的参数类型必须相同。如果从第一个形参设定了默认值之后的所有模板形参都要设定和第一个形参同类型的默认值。(声明:本人也是刚接触C++,以上只是我经过实例演示对原作者提出的一些质疑,可能我的示例有不到之处,还望大神们不吝赐教,共同完善此博客,给像我一样的菜鸟提供一个学习的平台!)