🐶博主主页:@ᰔᩚ. 一怀明月ꦿ
❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++
🔥座右铭:“不要等到什么都没有了,才下定决心去做”
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
目录
🐰类模版
🌸类模版的声明
🌸类模版的实例化
🌸类模版参数
🌸默认模版实参
🐰类模版
运用函数模版可以设计出与数据类型无关的通用函数。与之相似的,C++也支持用类模版来设计结构和成员函数,但是所处理数据类型不同的通用类。在设计模版类时,可以使其中的某些数据成员、成员函数的参数或返回值与具体类型无关。
模版在C++中更多的使用是在类的定义中,最常见的就是STL和ATL,它们都是作为C++标准集成在VC++开发环境中的标准模版库
🌸类模版的声明
类模版声明的格式:template<typename T1,typename T2,...> class 类名 { 类体 };
与函数模版一样,template是声明类模版的关键字,"<>"中T1、T2是类模版的类型参数。在一个类型模版中,可以定义多个不同的类型参数。"<>"中的typename和class可以互换,但与“类名”前的class具有不同的函数含义,二者没有关系。“类名”前的class表示类的声明
例如:下面的堆栈类模版Stack#include<iostream> using namespace std; const int SSize=10;//SSize为栈的容量 template<typename T> class Stack { public: Stack() { top=0; } void push(T e);//入栈 T pop();//出栈 bool stackEmpty()//判断栈是否为空 { return top==0; } bool stackFull()//判断栈是否为满 { return top==SSize; } private: T data[SSize]; int top; }; template<typename T> void Stack<T>::push(T e) { if(stackFull()) { cout<<"Stack is Full! Don't push data!"<<endl; return ; } data[top]=e; top++; } template<typename T> T Stack<T>::pop() { if(stackEmpty()) { cout<<"Stack is Empty! Don't pop data!"<<endl; return ; } top--; return data[top]; }
注意:
(1)类模版中的成员函数即可以在类模版内定义,也可以在类模版外定义
如果在类模版内定义成员函数,其与普通成员函数的定义方式一样,例如Stack的构造函数、stackEmpty、stackFull的定义
如果在类模版外定义成员函数,采用以下形式:template<模版参数列表> 返回值类型 类名<模版参数列表>::成员函数名(参数列表) { 函数体 }
例如:
Stack中的push成员函数
template<typename T> void Stack<T>::push(T e) { if(stackFull()) { cout<<"Stack is Full! Don't push data!"<<endl; return ; } data[top]=e; top++; }
一定注意:引用模版类名的地方,必须伴有该模版的参数列表(2)如果在类模版外将成员函数定义为inline(内联)函数,应将inline关键字加在类模版的声明后。例如:template<typename T> inline T Stack<T>::pop(){...}
(3)类模版的成员函数的定义必须同类模版的定义在同一个文件中。因为,类模版定义不同与类的定义,编译器无法通过一般的手段找到类模版的成员函数的代码,只有将它和类模版定义放一起,才能保证类模版正常使用。一般都放在一个头文件中(.h文件中)
🌸类模版的实例化
在声明一个类模版后,如何去使用它?例如:Stack<int>int_stack;
用Stack类模版定义了一个对象int_stack。编译器遇到该语句,会使用int去替换Stack类模版中的所有类型模版参数T,生成一个针对int类型数据的具体类,一般称为模版类,
如下:
class Stack { public: Stack() { top=0; } void push(int e);//入栈 int pop();//出栈 bool stackEmpty()//判断栈是否为空 { return top==0; } bool stackFull()//判断栈是否为满 { return top==SSize; } private: int data[SSize]; int top; };
由此可以看出,类模版、模版类以及模版对象之间的关系为:由类模版实例化生成针对具体数据类型的模版类,在由模版类定义模版对象。
用模版类定义对象形式:类模版名<实际模版参数列表>对象名; 类模版名<实际模版参数列表>对象名(构造函数的参数列表);
由类模版创建实例模版类时,必须为类模版的每个模版参数显式指定模版实参。然而,由函数模版创建其实例模版函数时,可以不显式指定模版实参。
注意:在类模版实例化的过程中,并不会实例化类模版的成员函数 ,也就是说,在用类模版定义对象时并不会生成类成员函数的代码。 只有成员函数被调用时才会被实例化,编译器才会生成其真正代码。🌸类模版参数
与函数模版的模版参数一样,类模版参数中也可以出现非类型参数 。对于前面堆栈类模版Stack,也可以不定义一个int型常变量SSize来指定栈容量大小,而改成为其增加一个非类型参数
#include<iostream> using namespace std; template<typename T,int SSize> class Stack { public: Stack() { top=0; } void push(T e);//入栈 T pop();//出栈 bool stackEmpty()//判断栈是否为空 { return top==0; } bool stackFull()//判断栈是否为满 { return top==SSize; } private: T data[SSize]; int top; }; template<typename T,int SSize> void Stack<T,SSize>::push(T e) { if(stackFull()) { cout<<"Stack is Full! Don't push data!"<<endl; return ; } data[top]=e; top++; } template<typename T,int SSize> T Stack<T,SSize>::pop() { if(stackEmpty()) { cout<<"Stack is Empty! Don't pop data!"<<endl; return ; } top--; return data[top]; }
当需要这个模版的一个实例时,模版类创建一个对象时,必须为非类型参数SSize显式提供一个编译时常数
例如:Stack<int,10>int_stack;
🌸默认模版实参
在类模版中,可以为模版提供默认实参。 例如,为了使上述固定大小的Stack类模版,可以为其非类型模版参数SSize提供默认参数例如:template<typename T,int SSize=10> class Stack { public: Stack() { top=0; } void push(T e);//入栈 T pop();//出栈 bool stackEmpty()//判断栈是否为空 { return top==0; } bool stackFull()//判断栈是否为满 { return top==SSize; } private: T data[SSize]; int top; };
类模版参数的默认实际参数是一个类型或值。当模版被实例化时,如果没有指定实际参数,则使用该类型或值。注意,默认实际参数应该是一个“对类模版实例的多数情况都适合”的类型或值。现在,如果创建一个Stack模版的对象时忽略了第二个模版参数,SSize的默认值就是10
注意:(1)作为默认的模版实参,它们只能被定义一次,编译器会知道第一次的模版声明或定义(2)指定默认实参的模版参数必须放在模版形参列表的右端,否则出错(和重载函数参数列表一样,带有默认值的参数必须放右端)
例如:
template<typename T1,typename T2,typename T3=int,int SSize=10>//正确 template<typename T1,typename T2=int,typename T3,int SSize=10>//错误
(3)可以为所有的模版参数提供实参但声明,如果用Stack类模版实例化一个对象时,如果全部都想使用模版参数的默认值,就必须使用一对尖括号,这样编译器就知道说明了一个类模版,例如:
#include<iostream> using namespace std; template<typename T=int,int SSize=10> class Stack { public: Stack() { top=0; } void push(T e);//入栈 T pop();//出栈 bool stackEmpty()//判断栈是否为空 { return top==0; } bool stackFull()//判断栈是否为满 { return top==SSize; } private: T data[SSize]; int top; }; template<typename T,int SSize>//这里千万不要写成template<typename T=int,int SSize=10>,因为作为默认的模版实参,它们只能被定义一次 void Stack<T,SSize>::push(T e) { if(stackFull()) { cout<<"Stack is Full! Don't push data!"<<endl; return ; } data[top]=e; top++; } template<typename T,int SSize>//这里千万不要写成template<typename T=int,int SSize=10>,因为作为默认的模版实参,它们只能被定义一次 T Stack<T,SSize>::pop() { if(stackEmpty()) { cout<<"Stack is Empty! Don't pop data!"<<endl; return 1; } top--; return data[top]; } int main() { Stack<>int_stack;//注意空的尖括号,这里全部是用类模版参数的默认值 int_stack.push(5); cout<<int_stack.pop()<<endl; return 0; } 结果: 5
类模版的模版形参列表中的参数类型有三种:类型参数、非类型参数和类模版的参数,函数模版的模版参数类型也与此相同。
🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸