C++中的类模版

简介: 🐰类模版🌸类模版的声明🌸类模版的实例化🌸类模版参数🌸默认模版实参

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

🐰类模版

🌸类模版的声明

🌸类模版的实例化

🌸类模版参数

🌸默认模版实参


🐰类模版

运用函数模版可以设计出与数据类型无关的通用函数。与之相似的,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. };

44DCD573-9893-4143-9E2E-F531B8F95B74.jpeg

 

由此可以看出,类模版、模版类以及模版对象之间的关系为:由类模版实例化生成针对具体数据类型的模版类,在由模版类定义模版对象。

用模版类定义对象形式:

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

类模版的模版形参列表中的参数类型有三种:类型参数、非类型参数和类模版的参数,函数模版的模版参数类型也与此相同。

🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸  

相关文章
|
2天前
|
编译器 C++
C++ 类构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
42 30
|
16天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)
|
1月前
|
存储 安全 编译器
【C++】类和对象(下)
【C++】类和对象(下)
【C++】类和对象(下)
|
16天前
|
C++
C++(十六)类之间转化
在C++中,类之间的转换可以通过转换构造函数和操作符函数实现。转换构造函数是一种单参数构造函数,用于将其他类型转换为本类类型。为了防止不必要的隐式转换,可以使用`explicit`关键字来禁止这种自动转换。此外,还可以通过定义`operator`函数来进行类型转换,该函数无参数且无返回值。下面展示了如何使用这两种方式实现自定义类型的相互转换,并通过示例代码说明了`explicit`关键字的作用。
|
16天前
|
存储 设计模式 编译器
C++(十三) 类的扩展
本文详细介绍了C++中类的各种扩展特性,包括类成员存储、`sizeof`操作符的应用、类成员函数的存储方式及其背后的`this`指针机制。此外,还探讨了`const`修饰符在成员变量和函数中的作用,以及如何通过`static`关键字实现类中的资源共享。文章还介绍了单例模式的设计思路,并讨论了指向类成员(数据成员和函数成员)的指针的使用方法。最后,还讲解了指向静态成员的指针的相关概念和应用示例。通过这些内容,帮助读者更好地理解和掌握C++面向对象编程的核心概念和技术细节。
|
30天前
|
存储 算法 编译器
c++--类(上)
c++--类(上)
|
1月前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
|
1月前
|
编译器 C++
virtual类的使用方法问题之静态和非静态函数成员在C++对象模型中存放如何解决
virtual类的使用方法问题之静态和非静态函数成员在C++对象模型中存放如何解决
|
1月前
|
编译器 C++
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
|
1月前
|
编译器 C++
【C++】类和对象(中)
【C++】类和对象(中)