第七章 类模板与向量
7.1 类模板
如果将类看作包含某些数据类型的框架,然后将这些数据类型从类中分离出来形成一个通用的数据类型T,为这个数据类型T设计一个操作集,并且允许原来那些数据类型的类都能使用这个操作集,这将避免因为类的数据类型不同而产生的重复性设计,其实,类型T并不是类,而是对类的描述,常称之为类模板。在编译时,由编译器将类模板与某种特定数据类型联系起来,就产生一个特定的类(模板类)。利用类模板能大大简化程序设计。
7.1.1 类模板基础知识
1.类模板的成分及语法
关键字class在这里的含义是“任意内部类型或用户定义类型”,但T也可能是结构或类。
对于函数模版及类模板来说,模板层次结构的大部分内容都是一样的,然而在模板声明之后,对类而言便显示出了根本性的差异。为了窗机类模板,在类模板参数表之后应该有类声明。在类中可以像使用其他类型(如int或double)那样使用模板参数。
类模板声明的一般方法如下:
template <类模板参数> class 类名{//类体};
2.类模板的对象
类模板也称为参数化类。初始化类模板时,只要传给它指定的数据类型,编译器就用指定类型替代模板参数产生相应的模板类。
用类模板定义对象的一般格式如下:
类名<模板实例化参数类型> 对象名(构造函数实参列表);
类名<模板实例化参数类型> 对象名;//默认或者无参数构造函数
使用模板类时,当给模板实例化参数类型一个指定的数据类型时,编译器自动用这个指定数据类型替代模板参数。
在类体外面定义成员函数时,必须用template重写类模板声明。一般格式如下:
template <模板参数>
返回类型 类名<模板类型参数>::成员函数名(函数参数列表){//函数体}
<模板类型参数> 是指template的“< >”内使用class(或typename)声明的类型参数,构造函数和析构函数没有返回类型。
模板实例化参数类型包括数据类型和值。编译器不能从构造函数参数列表推断出模板实例化参数类型,所以必须显式地给出对象的参数类型。
7.1.2 类模板的派生与继承
类模板也可以继承,继承的方法与普通的类一样。声明模板类继承之前,必须重新声明类模板。模板类的基类和派生类都可以是模板(或非模板)类。类模板的派生与继承很复杂,本书仅简单介绍模板类继承非模板类和从模板类派生一个类模板两种情况。
可以用一个非模板类为一组模板提供一种共同的实现方法。
7.2 向量与泛型算法
在数组生存期内,数组的大小是不会改变的。向量是一维数组的类版本,它与数组相似,其中的元素项总是连续存储的,但它和数组不同的是:向量中存储元素的多少可以在运行中根据需要动态地增长或缩小。向量是类模板,具有成员函数。
7.2.1 定义向量列表
向量(vector)类模板定义在头文件vector中,它提供4种构造函数,用来定义由各元素组成的列表。用length表示长度,数据类型用type表示,对象名为name。
向量定义的赋值运算符“=”,允许同类型的向量列表相互赋值,而不管它们的长度如何。向量可以改变赋值目标的大小,使它的元素数目与赋值源的元素数目相同。
向量的第一个元素也是从0开始。
不能使用列表初始化向量,但可以先初始化一个数组,然后把数组的内容复制给向量。
7.2.2 泛型指针
“与操作对象的数据类型相互独立”的算法称为泛型算法。也就是说,泛型算法提供了许多可用于向量的操作行为,而这些算法和想要操作的元素类型无关。这是借助一对泛型指针来实现的。向量具有指示第一元素的标记begin和指示结束的标记end,也就是标识要进行操作的元素空间。如果begin不等于end,算法便会首先作用于begin所指元素,并将begin前进一个位置,然后作用于当前begin所指元素,如此继续进行,直到begin等于end为止。因为end是最后一个元素的下一个位置,所以元素存在的范围是半开区间[begin,end)。
在向量中,泛型指针是在底层指针的行为之上提供一层抽象化机制,取代程序原来的“指针直接操作方式”。假设用T表示向量的参数化数据类型,iterator在STL里面是一种通用指针,它在向量中的作用相当于T*。用iterator声明向量的正向泛型指针的一般形式如下:
vector
对向量的访问可以是双向的,图中的rbegin和rend是提供给逆向泛型指针的开始和结束标志。逆向泛型指针的加操作是使它向rend方向移动,减操作向rbegin移动。声明逆向泛型指针使用reverse_iterator。声明的方法如下:
vector<数据类型> :: reverse_iterator 指针名;
7.2.3 向量的数据类型
向量除了可以使用基本数据类型之外,还可以使用构造类型,只要符合构成法则即可。
7.2.4 向量最基本的操作方法
1.访问向量容量信息的方法
(1)size():返回当前向量中已经存放的对象的个数
(2)max_size():返回向量可以容纳最多对象的个数,一般是操作系统的寻址空间所需容纳的对象的个数。这个参数不是用户指定的,它取决于硬件结构。
(3)capacity():返回无需再次分配内存就能容纳的对象个数。它的初始值为程序员最初申请的元素个数。当存放空间已满,又增加一个元素时,它在原来的基础上自动翻倍扩充空间,以便存放更多的元素。通俗地讲,也就是已申请的空间。这三者的关系如下。
(4)empty():当前向量为空时,返回true。
2.访问向量中对象的方法
(1)front():返回向量中的第一个对象。
(2)back():返回向量中的最后一个对象。
(3)operator[](size_type,n):返回向量中的第n+1个对象(下标为n的向量元素)。
3.在向量中插入对象的方法
(1)push_back(const T&):向向量尾部插入一个对象。
(2)insert(iterator it,const T&):向it所指向的向量位置前插入一个对象。
(3)insert(iterator it,size_type n,const T&X):向it所指向量位置前插入n个值为X的对象。
4.在向量中删除对象的方法
(1)pop_back(const T&):删除向量中最后一个对象。
(2)erase(iterator it):删除it所指向的容器对象。
(3)clear():删除向量中的所有对象,empty()返回true。
7.3 出圈游戏