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++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
61 2
|
2月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
111 5
|
2月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
111 4
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
148 4
|
3月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
35 4
|
3月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
33 4
|
3月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
31 1
|
3月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
3月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
3月前
|
存储 编译器 C语言
【C++类和对象(上)】—— 我与C++的不解之缘(三)
【C++类和对象(上)】—— 我与C++的不解之缘(三)