日期:2008-04-09  浏览次数:20406 次


侯捷观点(系列书评 2/2)

【Genericity/STL 大系】

《程序员》2001.02



作者简介:侯捷,台湾电脑技术作家,着译评兼擅。常着文章自娱,颇示己志。
个人网站:www.jjhou.com
北京镜站:www.csdn.net/expert/jjhou

如果有一项技术,可以让你的程式码处理各种不同的资料型别,甚至是目前未知的资料型别,你喜欢吗?

我会欣喜若狂。

基本上这就是「可复用性(reusibility)」的表现。当我们有新的资料型态产生,而过去完成的码完全无需修改即可沿用,不正是一种完美的「可复用性」吗?

物件导向技术中的多型(polymorphism),以及泛型技术中的泛型(genericity)都可以达到这个目标。它们的字义,也明白标示出其特色。对大多数人而言,polymorphism(多型技术)早已如雷灌耳,genericity(泛型技术)则稍感陌生。这是一个你有必要尽快进入的重要领域。


●勤前教育

数年前我第一次接触泛型程式设计(generic programming)与 STL(Standard Template Library)的时候,就深深被它吸引。虽然那时候我还不怎麽了解 STL 里头一大堆的术语像是 container、iterator、adaptor、function object、allocator┅。甚至连泛型技术深度依赖的基本技法 C++ template,当时的我都还只一知半解,但光只「泛型」这两个字就够把我吸引到那个世界里面了。

但愿我这麽说不至於误导你把泛型程式设计和 STL 划上等号。泛型概念滥觞於 Doug McIlroy 於 1968 年发表的一篇着名论文 "Mass Produced Software Components",那篇论文提出了 "reusable components"(可复用软体组件,又称为软体积木或软体 IC)的愿景。过去数十年中,泛型技术仍属於研究单位中的骄客,实作产品付之阙如。直到 C++ 不断加强 template 机制,并将 Alexander Stepanov 创作的 STL 纳入标准,泛型技术才终於在标准资料结构和标准演算法领域中有一套可被大众运用的实作品出现,向现实跨一大步。

让我们先复习一下。下面是多型的标准形式:
void func(Shape* ps)  // Shape 是某个 class 继承体系的基础类别
{
    // ...
    ps->draw();  // draw() 是个虚拟函式 (virtual function)
}


func() 的呼叫者可以自由传入 Shape 继承体系下任何一个 Shape 衍生类别的物件指标,func() 函式所唤起的将是实际传入之物件(指标)所对应的那个 draw() 虚拟函式。这种写法所带来的好处是,即使将来  Shape 继承体系衍生出前所未见的子型别,只要该子型别本身提供了 draw() 虚拟函式,上面这个 func() 就完全不必更改,可继续使用。

那麽,泛型又是什麽呢?简单地说,这是一种「将资料型别叁数化」的思维模式。C++ 的 template 机制就是泛型技术的一个具体载具。在 C++ 中,不论 functions 或是 classes,皆可将其中所需要的资料型别以一个保留器(placeholder)代表,这个保留器亦即所谓的 template 叁数。例如 function template:

template<typename T1, typename T2)
void func(T1 param1, T2 param2) { /* ... */ }


或是 class template:

template<typename T1, typename T2)
class A { /* ... */ }


从此,一旦程式中使用上述的 func() 或 class A,例如:

func(5, 2.3);
A<int, double> a;


编译器即根据 function template 的函式引数(上例的 5 和 2.3),或根据被我们明白标示出来的 class template 引数(上例的  int  和 double),自动推导出一份 function 实体或 class 实体。这个所谓的具现化动作(instantiation)在编译期就完成,不会增加执行期的成本。关於 template 的详细语法与性能,请叁考任何一本完备的 C++ 百科型书籍。

以上这种「将资料型别叁数化,再由编译器视使用当时的情况,为我们完成实体具现化」的概念,即是泛型的实际展现。template 是 C++ 语言中支援泛型概念的一个工具,而 STL 则是泛型概念的一套实作品。从学理上来说,STL 其实是一套严谨的 "concepts" 分类学。这里所谓的 concepts 有其严谨定义,意指「对某种型别的某些条件需求」。满足这些条件之型别,称为该 concept 的一个 model。举个例子,如果我们能够复制型别为 T 之物件,并可以将数值指派给 T 型别的变数身上,那麽型别 T 便符合 Assignable 这一 concept,而 T 便是 Assignable 的一个 model。STL 的六大组件 containers, algorithms, iterators, function objects, allocators, adaptors, 全都是 concepts,实作品如 vector, list, sort(), swap() 等等 templates, ... 全都是 models。

这样的学理概念,对大部份人勿宁是不可承受之重。大部份人只着眼 STL 的实用性,因为 STL 非常好用,弹性非常大,执行效率也很理想,可大幅提升软体开发效率。从实作的角度来看,以各种方式完成,符合 STL concepts 需求之各种 C++ classes 和 C++ functions,就是大家一般印象中的 STL,它们实际存在於各个相应的含入档中,如 <vector>,<functional>, <algorithms>.


●剖析 STL
任何学习,如果直接从抽象思维开始,对大部份人是一件困难的工作。通常我们需要一个具体可操作的东西,慢慢再由具象操作转为抽象思考。

那麽,先学会使用 STL,应该是学习泛型思维的最好途径。事实上,自从 STL 以及整个 C++ 标准程式库定案之後,很多专家,包括 Bjarne Stroustrup,都认为 C++ 程式语言的教学,首先应从如何使用标准程式库(含 STL)开始。
我当然无法在这篇文章中告诉你 STL 乃至整个标准程式库的用法。但是我可以给你一些概念,让你知道 STL 的架构。

STL 是一个完全以 template 技术完成的程式库。它构成了 C++ 标准程式库的绝大部份骨干 ─ 粗略估计应该占 80% 以上的面积。STL 有六大组件(components):

1 containers(容器),各种基本资料结构如 vector, list, deque, set, map┅,共约 11 种。其中有些亦被归类为 adaptors。