天天看点

templates(2.1)

class

templates

类别模板

就像上一章所说的 functions 那样,classes 也可以针对一或多个类型被参数化。用来管理「各种 不同类型的元素」的

container classes(容器类别)就是典型例子。运用 class templates 你可以实 作出可包容各种类型的 container

class。本章将以一个 stack class 作为 class templates

正如你所见,这个 class template 是以标准库的 class template vector<>

为基础实作出来的, 这样我们就可以不考虑内存管理、copy构造函数、assignment 运算符等等,从而得以把注意力放在这个 class template

的界面上。

3.1.1

class templates 的声明

声明class templates 的动作和声明function templates

类似:在声明语句之前加一条述句,以任意标识符声明类型参数(type parameters)。我们还是以t 作为标识符:

相同的规则再次适用:关键词 class

可以代替关键词 typename:

在 class template 内部,t

就像其它任意类型一样,可用来声明成员变量(member variables)和成员函数(member functions)。本例之中t

用来声明vector<> 所容纳的元素类型、声明一个「接受 t const&」的 push()函数、以及声明一个「返回类型为 t」的

top()函数:

这个 class 的类型为 stack<t>,t 是一个 template

parameter。现在,无论何时你以这个class声明变量或函数时,都应该写成 stack<t>。例如,假设你要声明自己的 copy

构造函数和assignment 运算符,可以写为:

然而如果只是需要class名称而不是class类型时,只需写stack

即可。构造函数和析构函数的声明就属于这种情况。

3.1.2

成员函数( member functions) 

作为了定义 class template 的成员函数,你必须指出它是个 function template,而且你必须使用

class template 的全称。因此 stack<t> 的成员函数 push()看起来便像这样:

这里调用了vector 的成员函数push_back(),把元素elem 追加到elems

尾端。注意,对一个vector进行 pop_back(),只是把最后一个元素移除,并不传回该元素。这种行为乃是基于异常安全性(exception

safety)考虑。实现一个「移除最后元素并传回,而且完全顾及异常安全性」的 pop() 是不可能的( 这个问题最早由 tom cargill

在[cargillexceptionsafety]中 讨论过),[sutterexceptional] 条款 10

也有讨论)。然而,如果抛开可能的危险,我们可以实作出一个「移除最后元素并传回」的pop(),但必须声明一个类型为 t 的区域变量:

当vector为空时,对它进行back()或pop_back()操作会导致未定义行为。所以我们必须在操作前先检查stack是否为空。如果stack为空,就抛出一个std::out_of_range

异常。top()之中也需进行相同检查;该函数传回 stack 的最后一个元素,但不移除之:

当然,你也可以把任何 class templates 的成员函数实作码写在 class

声明语句中,形成一个 inline函式。例如:

3.2

使用 class template stack

上例声明了 stack<int>这样一个类型,表示在该 class template 中 t 被替换为 int。于是

intstack成为这样一个object:内部使用「可容纳int 数据做为元素」的vector,并将被调用之任何成员函数都「以int

类型进行实例化」。同样道理,stack<std::string> 表示:stringstack

被创建为这样一个object:内部使用「可容纳string 数据做为元素」的vector,并将被调用之任何成员函数都「以std::string

类型进行实例化」。

注意,惟有被调用到的成员函数,才会被实例化(instantiated)。对class templates

而言,只有当某个成员函数被使用时,才会进行实例化。无疑地这么做可以节省时间和空间。另一个好处是,你甚至可以实例化一个class

template,而具现类型并不需要完整支持「class template 内与该

类型有关的所有操作」—前提是那些操作并未真正被叫用。举个例子,考虑某个class,其某些成员函数使用operator<

对内部元素排序;只要避免调用这些函数,就可以以一个「并不支持 operator<」的类型来实例化这个 class template。

本例中的 default 构造函数、push()函数和 top()函数都同时被 int 和 string

类型加以实现(实例化)。然而 pop()只被 string 实例化(译注:因为 pop()只被 stringstack 调用)。如果 class

template 拥有 static 成员,这些 static 成员会针对每一种被使用的类型完成实例化。

你可以像面对任何基本类型一样地使用一个实例化后的class template类型,前提是你的使用方式合法:

运用 typedef,你可以更方便地使用 class

templates:

注意,在c++中,typedef 并不产生新类型,只是为既有类型产生一个别名(type

alias)。因此

在以下述句之后: