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