C++中的类模板详细讲述

一、类模板定义及实例化

定义一个类模板:

1 template<class 模板参数表>2 3 class 类名{4 5 // 类定义......6 7 };

其中,template 是声明类模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个,可以是类型参数 ,也可以是非类型参数。类型参数由关键字class或typename及其后面的标识符构成。非类型参数由一个普通参数构成,代表模板定义中的一个常量。

例:

1 template<class type,int width>2 3 //type为类型参数,width为非类型参数4 5 class Graphics;

注意:

(1)如果在全局域中声明了与模板参数同名的变量,则该变量被隐藏掉。

(2)模板参数名不能被当作类模板定义中类成员的名字。

(3)同一个模板参数名在模板参数表中只能出现一次。

(4)在不同的类模板或声明中,模板参数名可以被重复使用。

 1 typedef string type; 2  3 template<class type,int width> 4  5 class Graphics 6  7 { 8  9 type node;//node不是string类型10 11 typedef double type;//错误:成员名不能与模板参数type同名12 13 };14 15 template<class type,class type>//错误:重复使用名为type的参数16 17 class Rect;18 19 template<class type> //参数名”type”在不同模板间可以重复使用20 21 class Round;


(5)
在类模板的前向声明和定义中,模板参数的名字可以不同。

 1 // 所有三个 Image 声明都引用同一个类模板的声明 2  3 template <class T> class Image; 4  5 template <class U> class Image; 6  7 // 模板的真正定义 8  9 template <class Type>10 11 class Image { //模板定义中只能引用名字”Type”,不能引用名字”T”和”U” };


(6)
类模板参数可以有缺省实参,给参数提供缺省实参的顺序是先右后左。

1 template <class type, int size = 1024>2 3 class Image;4 5 template <class type=double, int size >6 7 class Image;


(7)
类模板名可以被用作一个类型指示符。当一个类模板名被用作另一个模板定义中的类型指示符时,必须指定完整的实参表

 1 template<class type> 2  3 class Graphics 4  5 { 6  7 Graphics *next;//在类模板自己的定义中不需指定完整模板参数表 8  9 };10 11 template <calss type>12 13 void show(Graphics<type> &g)14 15 {16 17 Graphics<type> *pg=&g;//必须指定完整的模板参数表18 19 }


2.
类模板实例化

定义:从通用的类模板定义中生成类的过程称为模板实例化。

例:Graphics<int> gi;

类模板什么时候会被实例化呢?

当使用了类模板实例的名字,并且上下文环境要求存在类的定义时。

对象类型是一个类模板实例,当对象被定义时。此点被称作类的实例化点

一个指针或引用指向一个类模板实例,当检查这个指针或引用所指的对象时。

例:

 1 template<class Type> 2  3 class Graphics{}; 4  5 void f1(Graphics<char>);// 仅是一个函数声明,不需实例化 6  7 class Rect  8  9 {10 11   Graphics<double>& rsd;// 声明一个类模板引用,不需实例化12 13   Graphics<int> si;// si是一个Graphics类型的对象,需要实例化类模板14 15 }16 17 int main(){18 19   Graphcis<char>* sc;// 仅声明一个类模板指针,不需实例化20 21   f1(*sc);//需要实例化,因为传递给函数f1的是一个Graphics<int>对象。22 23   int iobj=sizeof(Graphics<string>);//需要实例化,因为sizeof会计算Graphics<string>对象的大小,为了计算大小,编译器必须根据类模板定义产生该类型。24 25 }


3.
非类型参数的模板实参

要点

绑定给非类型参数的表达式必须是一个常量表达式。

从模板实参到非类型模板参数的类型之间允许进行一些转换。包括左值转换、限定修饰转换、提升、整值转换。

可以被用于非类型模板参数的模板实参的种类有一些限制。

例:

二、类模板的成员函数

要点:

类模板的成员函数可以在类模板的定义中定义(inline函数),也可以在类模板定义之外定义(此时成员函数定义前面必须加上template及模板参数)。

类模板成员函数本身也是一个模板,类模板被实例化时它并不自动被实例化,只有当它被调用或取地址,才被实例化。

三、类模板的友元声明

类模板中可以有三种友元声明:

.非模板友元类或友元函数

2、绑定的友元类模板或函数模板。

3、非绑定的友元类模板或函数模板。

第二种声明表示类模板的实例和它的友元之间是一种一对一的映射关系。

如图:

第三种声明表示类模板的实例和它的友元之间是一种一对多的映射关系。

如图:

例:绑定的友元模板

例:非绑定的友元模板


注意
当把非模板类或函数声明为类模板友元时,它们不必在全局域中被声明或定义,但将一个类的成员声明为类模板友元,该类必须已经被定义,另外在声明绑定的友元类模板或函数模板时,该模板也必须先声明。

例:

四、类模板的静态数据成员、嵌套类型

.类模板的静态数据成员

要点:

静态数据成员的模板定义必须出现在类模板定义之外。

类模板静态数据成员本身就是一个模板,它的定义不会引起内存被分配,只有对其实例化才会分配内存。

当程序使用静态数据成员时,它被实例化,每个静态成员实例都与一个类模板实例相对应,静态成员的实例引用要通过一个类模板实例。

例:

2.类模板的嵌套类型

要点

在类模板中允许再嵌入模板,因此类模板的嵌套类也是一个模板,它可以使用外围类模板的模板参数。

当外围类模板被实例化时,它不会自动被实例化,只有当上下文需要它的完整类类型时,它才会被实例化。

公有嵌套类型可以被用在类定义之外,这时它的名字前必须加上类模板实例的名字。

例:

五、成员模板

定义:成员定义前加上template及模板参数表。

要点:

在一个类模板中定义一个成员模板,意味着该类模板的一个实例包含了可能无限多个嵌套类和无限多个成员函数.

只有当成员模板被使用时,它才被实例化.

成员模板可以定义在其外围类或类模板定义之外.

例:

注意:类模板参数不一定与类模板定义中指定的名字相同。

六、类模板的编译模式

1.包含编译模式

这种编译模式下,类模板的成员函数和静态成员的定义必须被包含在“要将它们实例化”的所有文件中,如果一个成员函数被定义在类模板定义之外,那么这些定义应该被放在含有该类模板定义的头文件中。

2.分离编译模式

这种模式下,类模板定义和其inline成员函数定义被放在头文件中,而非inline成员函数和静态数据成员被放在程序文本文件中。

例:

Setup的成员定义在User.c中不可见,但在这个文件中仍可调用模板实例Graphics<int>::Setup(const int &)。为实现这一点,须将类模声明为可导出的:当它的成员函数实例或静态数据成员实例被使用时,编译器只要求模板的定义,它的声明方式是在关键字template前加关键字export

.显式实例声明

当使用包含编译模式时,类模板成员的定义被包含在使用其实例的所有程序文本文件中,何时何地编译器实例化类模板成员的定义,我们并不能精确地知晓,为解决这个问题,标准C++提供了显式实例声明:关键字template后面跟着关键字class以及类模板实例的名字。

例:


显式实例化类模板时,它的所有成员也被显式实例化。

七、类模板的特化及部分特化

1.类模板的特化

先看下面的例子:

如果模板实参是Rect类型,我们不希望使用类模板Graphics的通用成员函数定义,来实例化成员函数out(),我们希望专门定义Graphics<Rect>::out()实例,让它使用Rect里面的成员函数。

为此,我们可以通过一个显示特化定义,为类模板实例的一个成员提供一个特化定义。

格式:template<> 成员函数特化定义

下面为类模板实例Graphics<Rect>的成员函数out()定义了显式特化:

Template<> void Graphics<Rect>::out(Rect figure){…}

注意:

只有当通用类模板被声明后,它的显式特化才可以被定义。

若定义了一个类模板特化,则必须定义与这个特化相关的所有成员函数或静态数据成员,此时类模板特化的成员定义不能以符号template<>作为打头。(template<>被省略)

类模板不能够在某些文件中根据通用模板定义被实例化,而在其他文件中却针对同一组模板实参被特化。

2.类模板部分特化

如果模板有一个以上的模板参数,则有些人就可能希望为一个特定的模板实参或者一组模板实参特化类模板,而不是为所有的模板参数特化该类模板。即,希望提供这样一个模板:它仍然是一个通用的模板,只不过某些模板参数已经被实际的类型或值取代。通过使用类模板部分特化,可以实现这一点。

例:


格式:
template<模板参数表>

注意:

部分特化的模板参数表只列出模板实参仍然未知的那些参数。

类模板部分特化是被隐式实例化的。编译器选择“针对该实例而言最为特化的模板定义”进行实例化,当没有特化可被使用时,才使用通用模板定义。

例:Graphics<24,90> figure;

它即能从通用类模板定义被实例化,也能从部分特化的定义被实例化,但编译器选择的是部分特化来实例化模板。

类模板部分特化必须有它自己对成员函数、静态数据成员和嵌套类的定义。

八、名字空间和类模板

类模板定义也可以被放在名字空间中。例如:

当类模板名字Graphics被用在名字空间之外时,它必须被名字空间名cplusplus_primer限定修,或者通过一个using声明或指示符被引入。例如:

注意:在名字空间中声明类模板也会影响该类模板及其成员的特化和部分特化声明的方式,类模板或类模板成员的特化声明必须被声明在定义通用模板的名字空间中(可以在名字空间之外定义模板特化)。

一个关于队列的例子,下面将其代码整理如下:

运行结果

时间: 2024-10-31 21:12:58

C++中的类模板详细讲述的相关文章

Android中多线程编程(四)AsyncTask类的详细解释(附源码)

Android中多线程编程中AsyncTask类的详细解释 1.Android单线程模型 2.耗时操作放在非主线程中执行 Android主线程和子线程之间的通信封装类:AsyncTask类 1.子线程中更新UI 2.封装.简化异步操作. 3.AsyncTask机制:底层是通过线程池来工作的,当一个线程没有执行完毕,后边的线程是无法执行的.必须等前边的线程执行完毕后,后边的线程才能执行. AsyncTask类使用注意事项: 1.在UI线程中创建AsyncTask的实例 2.必须在UI线程中调用As

C++:类模板与模板类

6.3 类模板和模板类 所谓类模板,实际上是建立一个通用类,其数据成员.成员函数的返回值类型和形参类型不具体指定,用一个虚拟的类型来代表.使用类模板定义对象时,系统会实参的类型来取代类模板中虚拟类型从而实现了不同类的功能. 定义一个类模板与定义函数模板的格式类似,必须以关键字template开始,后面是尖括号括起来的模板参数,然后是类名,其格式如下: template <typename 类型参数> class 类名{       类成员声明 }; 或者 template <class

类模板——stack类

不能将模板类的声明与实现分开到.h和.cpp中写类模板使用时必须将成员函数和实现写在一个头文件中,不能分开,不能分开,不能分开,重要的说三遍. stack.h 1 #ifndef STACK_H_ 2 #define STACK_H_ 3 template <class Type> 4 class Stack 5 { 6 private: 7 Type *ptr; 8 int num; 9 int size; 10 public: 11 Stack(const int &); 12 ~

c++数组类模板的实现

1.预备知识 (1)模板参数不仅仅可以是类型参数,还可以是数值型参数. 如: template <typename T, int N>//int N就是数值型参数, 要注意这个int类型,因为这里要注意数值型参数的限制,下面有说数值型参数的限制. void func() { T a[N];//使用模板参数来定义局部数组,模板的数值型参数来表示数组的大小. } func<double, 10>();//这样进行使用,数值型参数,也就是第二个参数要是常量,因为这是数值型模板参数的限制造

第58课 类模板的概念和意义

1. 类模板 (1)一些类主要用于存储和组织数据元素 (2)类中数据组织的方式和数据元素的具体类型无关.如数组类.链表类.Stack类.Queue类等. (3)C++中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能. 2. C++中的类模板 (1)以相同的方式处理不同的类型 (2)在类声明前使用template进行标识. (3)<typename T>用于说明类中使用的泛指类型T template <typename T> class Ope

泛函编程—模板函数_类模板

函数业务逻辑一样,只是函数参数类型不同函数模板的本质:类型参数化——泛型编程 语法: template <typename T> template <class T1,class T2>多个参数类型 类型 函数名(形式参数表) { 语句序列: } 函数模板基础: template是告诉C++编译器,开始泛型编程,看到T,不要随便报错 template <typename T>//一个模板 void myswap(T& a, T& b) { T c; c

类模板和函数模板引发的思考

首先我们在学习类模板和函数模板时候会遇到这样一个问题: 类模板 与模板类 函数模板与模板函数 这些不仅仅是简单的文字游戏,而是需要我们深深的区分一下才可以理解其中的奥秘! 再回想一下我们在学习C语言的时候也遇到了这样几个类似的名词 函数指针与指针函数 数组指针与指针数组 函数指针即是重点在后边的名词指针,前边的函数只是修饰名词指针的一个定语而已,欧,这是一个语文的奥秘哟,忽然觉得自己好博学,言归正传,重点在指针,那就是指向一个函数的指针,其中保存了这个函数的地址通过指针解引用可以调用这个函数 就

类模板友元函数坑死人不偿命的错误

错误例程: #include<iostream> using namespace std; template<class T> class Student { private: T age; public: Student(T age_) :age(age_){} friend bool operator==(const Student<T>& s1, const Student<T>& s2); }; int main() { Studen

类模板(四十八)

我们上节博客介绍了泛型编程思想,那么在 C++ 中是否可以将泛型的思想应用于类呢?答案肯定是显而易见的,在 C++ 中的标准库中,就是一些通用的类模板.我们先来看看类模板有哪些特性,它主要是用于存储和组织数据元素,类中数据组织的方式和数据元素的具体类型无关,如:数组类.链表类.Stack 类等.C++ 中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能. 在 C++ 中的类模板是以相同的方式处理不同的类型,并且在类声明前使用 template 进行标识.<