【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 3849|回复: 4

[STL] C++ STL编程入门基础

[复制链接]
发表于 2008-12-3 16:10:26 | 显示全部楼层 |阅读模式

学习笔记的内容摘自 http://www.stlchina.org/网站上的文章和学习资料。

1 初识STL:解答一些疑问

1.1 一个最关心的问题:什么是STL

如果说整个软件领域里,数十年来确实都在为了一个目标而奋斗--可复用性(reusability),这看起来似乎并不夸张。从最早的面向过程的函数库,到面向对象的程序设计思想,到各种组件技术(如:COM、EJB),到设计模式(design pattern)等等。而STL也在做着类似的事情,同时在它背后蕴涵着一种新的程序设计思想--泛型化设计(generic programming)。

STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最新的也是极具革命性的一部分。该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性。这种现象有些类似于Microsoft Visual C++中的MFC(Microsoft Foundation Class Library),或者是Borland C++ Builder中的VCL(Visual Component Library),对于此二者,大家一定不会陌生吧。

从逻辑层次来看,在STL中体现了泛型化程序设计的思想(generic programming),引入了诸多新的名词,比如像需求(requirements),概念(concept),模型(model),容器(container),算法(algorithmn),迭代子(iterator)等。与OOP(object-oriented programming)中的多态(polymorphism)一样,泛型也是一种软件的复用技术。

从实现层次看,整个STL是以一种类型参数化(type parameterized)的方式实现的,这种方式基于一个在早先C++标准中没有出现的语言特性--模板(template)。如果查阅任何一个版本的STL源代码,你就会发现,模板作为构成整个STL的基石是一件千真万确的事情。除此之外,还有许多C++的新特性为STL的实现提供了方便。

1.2 追根溯源:STL的历史

在结识新朋友的时候,大多数人总是忍不住想了解对方的过去。本节将带您简单回顾一下STL的过去。

被誉为STL之父的 Alexander Stepanov,出生于苏联莫斯科,早在20世纪70年代后半期,他便已经开始考虑,在保证效率的前提下,将算法从诸多具体应用之中抽象出来的可能性,这便是后来泛型化思想的雏形。为了验证自己的思想,他和纽约州立大学教授Deepak Kapur,伦塞里尔技术学院教授David Musser共同开发了一种叫做Tecton的语言。尽管这次尝试最终没有取得实用性的成果,但却给了Stepanov很大的启示。

在随后的几年中,他又和David Musser等人先后用Schema语言(一种Lisp语言的变种)和Ada语言建立了一些大型程序库。这其间,Alexander Stepanov开始意识到,在当时的面向对象程序设计思想中所存在的一些问题,比如抽象数据类型概念所存在的缺陷。Stepanov希望通过对软件领域中各组成部分的分类,逐渐形成一种软件设计的概念性框架。

1987年左右,在贝尔实验室工作的Alexander Stepanov开始首次采用C++语言进行泛型软件库的研究。但遗憾的是,当时的C++语言还没有引入模板(template)的语法,现在我们可以清楚的看到,模板概念之于STL实现,是何等重要。是时使然,采用继承机制是别无选择的。尽管如此,Stepanov还是开发出了一个庞大的算法库。与此同时,在与Andrew Koenig(前ISO C++标准化委员会主席)和Bjarne Stroustrup(C++语言的创始人)等顶级大师们的共事过程中,Stepanov开始注意到C/C++语言在实现其泛型思想方面所具有的潜在优势。就拿C/C++中的指针而言,它的灵活与高效运用,使后来的STL在实现泛型化的同时更是保持了高效率。另外,在STL中占据极其重要地位的迭代子概念便是源自于C/C++中原生指针( native pointer)的抽象。

1988年,Alexander Stepanov开始进入惠普的Palo Alto实验室工作,在随后的4年中,他从事的是有关磁盘驱动器方面的工作。直到1992年,由于参加并主持了实验室主任Bill Worley所建立的一个有关算法的研究项目,才使他重新回到了泛型化算法的研究工作上来。项目自建立之后,参与者从最初的8人逐渐减少,最后只剩下两个人--Stepanove本人和Meng Lee。经过长时间的努力,最终,信念与汗水所换来的是一个包含有大量数据结构和算法部件的庞大运行库。这便是现在的STL的雏形(同时也是STL的一个实现版本--HP STL)。

1993年,当时在贝尔实验室的Andrew Koenig看到了Stepanove的研究成果,很是兴奋。在他的鼓励与帮助下,Stepanove于是年9月的圣何塞为ANSI/ISO C++标准委员会做了一个相关演讲(题为"The Science of C++ Programming"),向委员们讲述了其观念。然后又于次年3月,在圣迭戈会议上,向委员会提交了一份建议书,以期使STL成为C++标准库的一部分。尽管这一建议十分庞大,以至于降低了被通过的可能性,但由于其所包含的新思想,投票结果以压倒多数的意见认为推迟对该建议的决定。

随后,在众人的帮助之下,包括Bjarne Stroustrup在内,Stepanove又对STL进行了改进。同时加入了一个封装内存模式信息的抽象模块,也就是现在STL中的 allocator,它使STL的大部分实现都可以独立于具体的内存模式,从而独立于具体平台。在同年夏季的滑铁卢会议上,委员们以80%赞成,20%反对,最终通过了提案,决定将STL正式纳入C++标准化进程之中,随后STL便被放进了会议的工作文件中。自此,STL终于成为了C++家族中的重要一员。

此后,随着C++标准的不断改进,STL也在不断地作着相应的演化。直至1998年,ANSI/ISO C++标准正式定案,STL始终是C++标准中不可或缺的一大部件。

1.3 千丝万缕的联系

1.3.1 STL和C++

没有C++语言就没有STL,这么说毫不为过。一般而言,STL作为一个泛型化的数据结构和算法库,并不牵涉具体语言(当然,在C++里,它被称为 STL)。也就是说,如果条件允许,用其他语言也可以实现之。这里所说的条件,主要是指类似于"模板"这样的语法机制。如果你没有略过前一节内容的话,应该可以看到,Alexander Stepanov在选择C++语言作为实现工具之前,早以采用过多种程序设计语言。但是,为什么最终还是C++幸运的承担了这个历史性任务呢?原因不仅在于前述那个条件,还在于C++在某些方面所表现出来的优越特性,比如:高效而灵活的指针。但是如果把C++作为一种OOP(Object- Oriented Programming,面向对象程序设计)语言来看待的话(事实上我们一般都是这么认为的,不是吗?),其功能强大的继承机制却没有给STL的实现帮上多大的忙。在STL的源代码里,并没有太多太复杂的继承关系。继承的思想,甚而面向对象的思想,还不足以实现类似STL这样的泛型库。C++只有在引入了 "模板"之后,才直接导致了STL的诞生。这也正是为什么,用其他比C++更纯的面向对象语言无法实现泛型思想的一个重要原因。当然,事情总是在变化之中,像Java在这方面,就是一个很好的例子,jdk1.4中已经加入了泛型的特性。

此外,STL对于C++的发展,尤其是模板机制,也起到了促进作用。比如:模板函数的偏特化(template function partial specialization),它被用于在特定应用场合,为一般模板函数提供一系列特殊化版本。这一特性是继STL被ANSI/ISO C++标准委员会通过之后,在Bjarne和Stepanov共同商讨之下并由Bjarne向委员会提出建议的,最终该项建议被通过。这使得STL中的一些算法在处理特殊情形时可以选择非一般化的方式,从而保证了执行的效率。

1.3.2 STL和C++标准函数库

STL是最新的C++标准函数库中的一个子集,这个庞大的子集占据了整个库的大约80%的分量。而作为在实现STL过程中扮演关键角色的模板则充斥了几乎整个C++标准函数库。在这里,我们有必要看一看C++标准函数库里包含了哪些内容,其中又有哪些是属于标准模板库(即STL)的。

C++标准函数库为C++程序员们提供了一个可扩展的基础性框架。我们从中可以获得极大的便利,同时也可以通过继承现有类,自己编制符合接口规范的容器、算法、迭代子等方式对之进行扩展。它大致包含了如下几个组件

C标准函数库,基本保持了与原有C语言程序库的良好兼容,尽管有些微变化。人们总会忍不住留恋过去的美好岁月,如果你曾经是一个C程序员,对这一点一定体会颇深。或许有一点会让你觉得奇怪,那就是在C++标准库中存在两套C的函数库,一套是带有.h扩展名的(比如),而另一套则没有(比如)。它们确实没有太大的不同

语言支持(language support)部分,包含了一些标准类型的定义以及其他特性的定义,这些内容,被用于标准库的其他地方或是具体的应用程序中。

诊断(diagnostics)部分,提供了用于程序诊断和报错的功能,包含了异常处理(exception handling),断言(assertions),错误代码(error number codes)三种方式。

通用工具(general utilities)部分,这部分内容为C++标准库的其他部分提供支持,当然你也可以在自己的程序中调用相应功能。比如:动态内存管理工具,日期/时间处理工具。记住,这里的内容也已经被泛化了(即采用了模板机制)。

字符串(string)部分,用来代表和处理文本。它提供了足够丰富的功能。事实上,文本是一个string对象,它可以被看作是一个字符序列,字符类型可能是char,或者wchar_t等等。string可以被转换成char*类型,这样便可以和以前所写的C/C++代码和平共处了。因为那时侯除了 char*,没有别的。

国际化(internationalization)部分,作为OOP特性之一的封装机制在这里扮演着消除文化和地域差异的角色,采用locale和facet可以为程序提供众多国际化支持,包括对各种字符集的支持,日期和时间的表示,数值和货币的处理等等。毕竟,在中国和在美国,人们表示日期的习惯是不同的。

容器(containers)部分,STL的一个重要组成部分,涵盖了许多数据结构,比如前面曾经提到的链表,还有:vector(类似于大小可动态增加的数组)、queue(队列)、stack(堆栈)……。string 也可以看作是一个容器,适用于容器的方法同样也适用于string。现在你可以轻松的完成数据结构课程的家庭作业了。

算法(algorithms)部分,STL的一个重要组成部分,包含了大约70个通用算法,用于操控各种容器,同时也可以操控内建数组。比如:find用于在容器中查找等于某个特定值的元素,for_each用于将某个函数应用到容器中的各个元素上,sort用于对容器中的元素排序。所有这些操作都是在保证执行效率的前提下进行的,所以,如果在你使用了这些算法之后程序变得效率底下,首先一定不要怀疑这些算法本身,仔细检查一下程序的其他地方。

迭代器(iterators)部分,STL的一个重要组成部分,如果没有迭代器的撮合,容器和算法便无法结合的如此完美。事实上,每个容器都有自己的迭代器,只有容器自己才知道如何访问自己的元素。它有点像指针,算法通过迭代器来定位和操控容器中的元素。

数值(numerics)部分,包含了一些数学运算功能,提供了复数运算的支持。

输入/输出(input/output)部分,就是经过模板化了的原有标准库中的iostream部分,它提供了对C++程序输入输出的基本支持。在功能上保持了与原有iostream的兼容,并且增加了异常处理的机制,并支持国际化(internationalization)。

总体上,在C++标准函数库中,STL主要包含了容器、算法、迭代器。string也可以算做是STL的一部分。

图1:STL和C++标准函数库

 

1.4 STL的不同实现版本

 楼主| 发表于 2008-12-3 16:11:01 | 显示全部楼层

1.4 STL的不同实现版本

1.4.1 HP STL

HP STL是所有其它STL实现版本的根源。它是STL之父Alexander Stepanov在惠普的Palo Alto实验室工作时,和Meng Lee共同完成的,是第一个STL的实现版本(参见1.2节)。这个STL是开放源码的,所以它允许任何人免费使用、复制、修改、发布和销售该软件和相关文档,前提是必须在所有相关文件中加入HP STL的版本信息和授权信息。现在已经很少直接使用这个版本的STL了。

1.4.2 P.J. Plauger STL

P. J. Plauger STL属于个人作品,由P. J. Plauger本人实现,是HP STL的一个继承版本,因此在其所有头文件中都含有HP STL的相关声明,同时还有P. J. Plauger本人的版权声明。P. J. Plauger是标准C中stdio库的早期实现者,现在是C/C++ User's Journal的主编,与Microsoft保持着良好的关系。P. J. Plauger STL便是被用于Microsoft的Visual C++中的。在Windows平台下的同类版本中,其性能不错,但是queue组件(队列,一种容器)的效率不理想,同时由于Visual C++对C++语言标准的支持不是很好(至少直到VC6.0为止,还是如此),因此一定程度上影响了P. J. Plauger STL的性能。此外,该版本的源代码可读性较差,你可以在VC的Include子目录下找到所有源文件(比如:Crogram FilesMicrosoft Visual StudioVC98Include)。因为不是开放源码的(open source),所以这些源代码是不能修改和销售的,目前P.J. Plauger STL由Dinkumware公司提供相关服务,详情请见http://www.dinkumware.com。据称Visual Studio.NET中的Visual C++.NET(即VC7.0),对C++标准的支持有所提高,并且多了以哈希表(hash table)为基础而实现的map容器,multimap容器和set容器。

1.4.3 Rouge Wave STL

Rouge Wave STL是由Rouge Wave公司实现的,也是HP STL的一个继承版本,除了HP STL的相关声明之外,还有Rouge Wave公司的版权声明。同时,它也不是开放源码的,因此无法修改和销售。该版本被Borland C++ Builder所采用,你可以在C++ Builder的Include子目录下找到所有头文件(比如:Crogram FilesBorlandCbuilder5Include)。尽管Rouge Wave STL的性能不是很好,但由于C++ Builder对C++语言标准的支持还算不错,使其表现在一定程度上得以改善。此外,其源代码的可读性较好。可以从如下网站得到更详细的情况介绍: http://www.rougewave.com。遗憾的是该版本已有一段时间没有更新且不完全符合标准。因此在Borland C++ Builder 6.0中,它的地位被另一个STL的实现版本--STLport(见后)取代了。但是考虑到与以前版本的兼容,C++ Builder 6.0还是保留了Rouge Wave STL,只是如果你想查看它的源代码的话,需要在别的目录中才能找到(比如:Crogram FilesBorlandCbuilder6Includeoldstl)。

1.4.4 STLport

STLport最初源于俄国人Boris Fomitchev的一个开发项目,主要用于将SGI STL的基本代码移植到其他诸如C++Builder或者是Visual C++这样的主流编译器上。因为SGI STL属于开放源码,所以STLport才有权这样做。目前STLport的最新版本是4.5。可以从如下网站得到更详细的情况介绍://www.stlport.org,可以免费下载其源代码。STLport已经被C/C++技术委员会接受成为工业标准,且在许多平台上都支持。根据测试STLport的效率比VC中的STL要快。比Rouge Wave STL更符合标准,也更容易移植。Borland C++ Builder已经在其6.0版中加入了对STLport的支持,它使用的STLport就是4.5版的,C++ Builder 6.0同时还提供了STLport的使用说明。你可以在C++ Builder的IncludeStlport子目录下找到所有头文件(比如:Crogram FilesBorlandCbuilder6IncludeStlport)。

1.4.5 SGI STL

SGI STL是由Silicon Graphics Computer System, Inc公司实现的,其设计者和编写者包括Alexander Stepanov和Matt Austern,同样它也是HP STL的一个继承版本。它属于开放源码,因此你可以修改和销售它。SGI STL被GCC(linux下的C++编译器)所采用,你可以在GCC的Include子目录下找到所有头文件(比如:C:cygnuscygwin -b20includeg++include)。由于GCC对C++语言标准的支持很好,SGI STL在linux平台上的性能相当出色。此外,其源代码的可读性也很好。可以从如下网站得到更详细的情况介绍:http://www.sgi.com,可以免费下载其源代码。目前的最新版本是3.3。

********************************************************************************************************************************************

好晚了,只是另外一台电脑在装vs2005,我想等它装完了再走而已。唉,每天到下班的时候就发现找到好的学习的地方了呢。明天继续努力。

其实今天又看到了一个令人惊动的字眼:1998年STL正式发布,距今天已经9年了呢。  

2 牛刀小试:且看一个简单例程

大致功能是:从标准输入设备(一般是键盘)读入一些整型数据,然后对它们进行排序,最终将结果输出到标准输出设备(一般是显示器屏幕)。这是一种典型的处理方式,程序本身具备了一个系统所应该具有的几乎所有的基本特征:输入 + 处理 + 输出。你将会看到三个不同版本的程序。第一个是没有使用STL的普通C++程序,你将会看到完成这样看似简单的事情,需要花多大的力气,而且还未必没有一点问题(真是吃力不讨好)。第二个程序的主体部分使用了STL特性,此时在第一个程序中所遇到的问题就基本可以解决了。同时,你会发现采用了STL之后,程序变得简洁明快,清晰易读。第三个程序则将STL的功能发挥到了及至,你可以看到程序里几乎每一行代码都是和STL相关的。

2.2.1 第一版:史前时代--转木取火

在STL还没有降生的"黑暗时代",C++程序员要完成前面所提到的那些功能,需要做很多事情(不过这比起C程序来,似乎好一点),程序大致是如下这个样子的:

 

// name:example2_1.cpp
// alias:Rubish

#include <stdlib.h>
#include <iostream.h>

int compare(const void *arg1, const void *arg2);

void main(void)
{
const int max_size = 10; // 数组允许元素的最大个数
int num[max_size]; // 整型数组

// 从标准输入设备读入整数,同时累计输入个数,
// 直到输入的是非整型数据为止
int n;
for (n = 0; cin >> num[n]; n ++);

// C标准库中的快速排序(quick-sort)函数
qsort(num, n, sizeof(int), compare);

// 将排序结果输出到标准输出设备
for (int i = 0; i < n; i ++)
cout << num << "\n";
}

// 比较两个数的大小,
// 如果*(int *)arg1比*(int *)arg2小,则返回-1
// 如果*(int *)arg1比*(int *)arg2大,则返回1
// 如果*(int *)arg1等于*(int *)arg2,则返回0
int compare(const void *arg1, const void *arg2)
{
return (*(int *)arg1 < *(int *)arg2) ? -1 :
(*(int *)arg1 > *(int *)arg2) ? 1 : 0;
}

这是一个和STL没有丝毫关系的传统风格的C++程序。因为程序的注释已经很详尽了,所以不需要我再做更多的解释。总的说来,这个程序看起来并不十分复杂。只是,那个compare函数,看起来有点费劲。指向它的函数指针被作为最后一个实参传入qsort函数,qsort是C程序库stdlib.h中的一个函数。以下是qsort的函数原型:

void qsort(void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) ); 看起来有点令人作呕,尤其是最后一个参数。大概的意思是,第一个参数指明了要排序的数组(比如:程序中的num),第二个参数给出了数组的大小(qsort没有足够的智力预知你传给它的数组的实际大小),第三个参数给出了数组中每个元素以字节为单位的大小。以下是某次运行的结果:

输入:0 9 2 1 5

输出:0 1 2 5 9有一个问题,这个程序并不像看起来那么健壮(Robust)。如果我们输入的数字个数超过max_size所规定的上限,就会出现数组越界问题。如果你在Visual C++的IDE环境下以控制台方式运行这个程序时,会弹出非法内存访问的错误对话框。这个问题很严重,严重到足以使你开始重新审视这个程序的代码。为了弥补程序中的这一缺陷。我们不得不考虑采用如下三种方案中的一种:

  1. 采用大容量的静态数组分配。
  2. 限定输入的数据个数。
  3. 采用动态内存分配。

第一种方案比较简单,你所做的只是将max_size改大一点,比如:1000或者10000。但是,严格讲这并不能最终解决问题,隐患仍然存在。假如有人足够耐心,还是可以使你的这个经过纠正后的程序崩溃的。此外,分配一个大数组,通常是在浪费空间,因为大多数情况下,数组中的一部分空间并没有被利用。

再来看看第二种方案,通过在第一个for循环中加入一个限定条件,可以使问题得到解决。比如:for (int n = 0; cin >> num[n] && n < max_size; n ++); 但是这个方案同样不甚理想,尽管不会使程序崩溃,但失去了灵活性,你无法输入更多的数。

看来只有选择第三种方案了。是的可以利用指针,以及动态内存分配妥善的解决上述问题,并且使程序具有良好的灵活性。这需要用到new,delete操作符,或者古老的malloc(), realloc()和free()函数。但是为此,你将牺牲程序的简洁性,使程序代码陡增,代码的处理逻辑也不再像原先看起来那么清晰了。一个 compare函数或许就已经令你不耐烦了,更何况要实现这些复杂的处理机制呢?很难保证你不会在处理这个问题的时候出错,很多程序的bug往往就是这样产生的。同时,你还应该感谢stdlib.h,它为你提供了qsort函数,否则,你还需要自己实现排序算法。如果你用的是冒泡法排序,那效率就不会很理想。……,问题真是越来越让人头疼了!

2.2.2 第二版:工业时代--组件化大生产

试着使用一下STL,看看效果如何。

 

// name:example2_2.cpp
// alias:The first STL program

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void main(void)
{
vector<int> num; // STL中的vector容器
int element;

// 从标准输入设备读入整数,
// 直到输入的是非整型数据为止
while (cin >> element)
num.push_back(element);

// STL中的排序算法
sort(num.begin(), num.end());

// 将排序结果输出到标准输出设备
for (int i = 0; i < num.size(); i ++)
cout << num << "\n";
}

这个程序的主要部分改用了STL的部件,看起来要比第一个程序简洁一点,你已经找不到那个讨厌的compare函数了。这个程序是足够健壮的。

程序的前三行是包含的头文件,它们提供了程序所要用到的所有C++特性(包括输入输出处理,STL中的容器和算法)。不必在意那个.h,并不是我的疏忽,程序保证可以编译通过,只要你的C++编译器支持标准C++规范的相关部分。

第四行加入那个声明只是为了表明程序引用到了std这个标准名字空间(namespace),因为STL中的那些玩意儿全都包含在那里面。只有通过这行声明,编译器才能允许你使用那些有趣的特性。

程序中用到了vector,它是STL中的一个标准容器,可以用来存放一些元素。你可以把vector理解为int [?],一个整型的数组。之所以大小未知是因为,vector是一个可以动态调整大小的容器,当容器已满时,如果再放入元素则vector会悄悄扩大自己的容量。push_back是vector容器的一个类属成员函数,用来在容器尾端插入一个元素。main函数中第一个while循环做的事情就是不断向 vector容器尾端插入整型数据,同时自动维护容器空间的大小。

sort是STL中的标准算法,用来对容器中的元素进行排序。它需要两个参数用来决定容器中哪个范围内的元素可以用来排序。这里用到了vector的另两个类属成员函数。begin()用以指向vector的首端,而end()则指向vector的末端。这里有两个问题,begin()和end()的返回值是什么?这涉及到STL的另一个重要部件--迭代器(Iterator),不过这里并不需要对它做详细了解。你只需要把它当作是一个指针就可以了,一个指向整型数据的指针。相应的sort函数声明也可以看作是void sort(int* first, int* last),尽管这实际上很不精确。另一个问题是和end()函数有关,尽管前面说它的返回值指向vector的末端,但这种说法不能算正确。事实上,它的返回值所指向的是vector中最末端元素的后面一个位置,即所谓pass-the-end value。这听起来有点费解,不过不必在意,这里只是稍带一提。总的来说,sort函数所做的事情是对那个准整型数组中的元素进行排序,一如第一个程序中的那个qsort,不过比起qsort来,sort似乎要简单了许多。

程序的最后是输出部分,在这里vector完全可以以假乱真了,它所提供的对元素的访问方式简直和普通的C++内建数组一模一样。那个size函数用来返回vector中的元素个数,就相当于第一个程序中的变量n。这两行代码直观的不用我再多解释了。

2.2.3 第三版:唯美主义的杰作

事态的发展有时候总会趋向极端,这在那些唯美主义者当中犹是如此。首先声明,我并不是一个唯美主义者,提供第二版程序的改进版,完全是为了让你更深刻的感受到STL的魅力所在。在看完第三版之后,你会强烈感受到这一点。或许你也会变成一个唯美主义者了,至少在STL方面。这应该不是我的错,因为决定权在你手里。下面我们来看看这个绝版的C++程序。

// name:example2_3.cpp
// alias:aesthetic version

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

using namespace std;

void main(void)
{
typedef vector<int> int_vector;
typedef istream_iterator<int> istream_itr;
typedef ostream_iterator<int> ostream_itr;
typedef back_insert_iterator< int_vector > back_ins_itr;

// STL中的vector容器
int_vector num;

// 从标准输入设备读入整数,
// 直到输入的是非整型数据为止
copy(istream_itr(cin), istream_itr(), back_ins_itr(num));

// STL中的排序算法
sort(num.begin(), num.end());

// 将排序结果输出到标准输出设备
copy(num.begin(), num.end(), ostream_itr(cout, "\n"));
}
 楼主| 发表于 2008-12-3 16:12:46 | 显示全部楼层
2.3 如何运行 
选用了目前在Windows平台下较为常见的Microsoft Visual C++ 6.0和Borland C++ Builder 6.0作为例子。
尽管Visual C++ 6.0对最新的ANSI/ISO C++标准支持的并不是很好。不过据称Visual C++ .NET(也就是VC7.0)
在这方面的性能有所改善。 
你可以选用多种方式运行前面的程序,比如在Visual C++下,你可以直接在DOS命令行状态下编译运行,也可以在VC的
IDE下采用控制台应用程序(Console Application)的方式运行。对于C++ Builder,情况也类似。 
对于Visual C++而言,如果是在DOS命令行状态下,你首先需要找到它的编译器。假定你的Visual C++装在
C:\Program Files\Microsoft Visual Studio\VC98下面,则其编译器所在路径应该是
C:\Program Files\Microsoft Visual Studio\VC98\Bin,
在那里你可以找到cl.exe文件。编译时请加上/GX和/MT参数。如果一切正常,结果就会产生一个可执行文件。如下所示: 
cl /GX /MT example2_2.cpp 
前一个参数用于告知编译器允许异常处理(Exception Handling)。在P. J. Plauger STL中的很多地方使用了
异常处理机制(即try…throw…catch语法),所以应该加上这个参数,否则会有如下警告信息: 
warning C4530: C++ exception handler used, but unwind semantics are not enabled. 
后一个参数则用于使程序支持多线程,它需要在链接时使用LIBCMT.LIB库文件。不过P. J. Plauger STL
并不是线程安全的(thread safety)。如果你是在VC环境下使用像STLport这样的STL实现版本,
则需要加上这个参数,因为STLport是线程安全的。 
如果在IDE环境下,可以在新建工程的时候选择控制台应用程序.

至于那些参数的设置,则可以通过在Project功能菜单项中的Settings功能【Alt+F7】中设置编译选项来完成。 
有时,在IDE环境下编译STL程序时,可能会出现如下警告信息(前面那几个示例程序不会出现这种情况): 
warning C4786: '……' : identifier was truncated to '255' characters in the debug information 
这是因为编译器在Debug状态下编译时,把程序中所出现的标识符长度限制在了255个字符范围内。如果超过最大长度,这些标识
符就无法在调试阶段查看和计算了。而在STL程序中大量的用到了模板函数和模板类,编译器在实例化这些内容时,展开之后所产
生的标识符往往很长(没准会有一千多个字符!)。如果你想认识一下这个warning的话,很简单,在程序里加上如下一行代码: 
vector<string> string_array; // 类似于字符串数组变量 
对于这样的warning,当然可以置之不理,不过也是有解决办法的。 你可以在文件开头加入下面这一行
#pragma warning(disable: 4786)。
它强制编译器忽略这个警告信息,这种做法虽然有点粗鲁,但是很有效。

study 的版本是 Release 3.3: June 8, 2000

SGI STL被GCC(linux下的C++编译器)所采用,你可以在GCC的Include子目录下找到所有头文件(比如:C:\cygnus\cygwin -b20\include\g++\include)。

Which compilers are supported?
The STL has been tested on these compilers: SGI 7.1 and later, or 7.0 with the -n32 or -64 flag; gcc 2.8 or egcs 1.x; Microsoft 5.0 and later.

Introduction to the Standard Template Library

The Standard Template Library, or STL, is a C++ library of container classes, algorithms, and iterators; it provides many of the basic algorithms and data structures of computer science. The STL is a generic library, meaning that its components are heavily parameterized: almost every component in the STL is a template.

Containers and algorithms

Like many class libraries, the STL includes container classes: classes whose purpose is to contain other objects. The STL includes the classes vector, list, deque, set, multiset, map, multimap, hash_set, hash_multiset, hash_map, and hash_multimap. Each of these classes is a template, and can be instantiated to contain any type of object. You can, for example, use a vector<int> in much the same way as you would use an ordinary C array, except that vector eliminates the chore of managing dynamic memory allocation by hand.

      vector<int> v(3);            // Declare a vector of 3 elements.
      v[0] = 7;
      v[1] = v[0] + 3;
      v[2] = v[0] + v[1];          // v[0] == 7, v[1] == 10, v[2] == 17  

The STL also includes a large collection of algorithms that manipulate the data stored in containers. You can reverse the order of elements in a vector, for example, by using the reverse algorithm.

      reverse(v.begin(), v.end()); // v[0] == 17, v[1] == 10, v[2] == 7

The reason for both of these facts is the same: reverse, like other STL algorithms, is decoupled from the STL container classes. This means that reverse can be used not only to reverse elements in vectors, but also to reverse elements in lists, and even elements in C arrays. The following program is also valid.

      double A[6] = { 1.2, 1.3, 1.4, 1.5, 1.6, 1.7 };
      reverse(A, A + 6);
      for (int i = 0; i < 6; ++i)
        cout << "A[" << i << "] = " << A;

This example uses a range, just like the example of reversing a vector: the asymmetrical notation is a reminder that the two endpoints are different, that the first is the beginning of the range and the second is one past the end of the range.

Iterators

what exactly does reverse declare its arguments to be, and what exactly do v.begin() and v.end() return?

The answer is that the arguments to reverse are iterators, which are a generalization of pointers. Iterators are the mechanism that makes it possible to decouple algorithms from containers: algorithms are templates, and are parameterized by the type of iterator, so they are not restricted to a single type of container. Consider, for example, how to write an algorithm that performs linear search through a range. This is the STL's find algorithm.

      template <class InputIterator, class T>
      InputIterator find(InputIterator first, InputIterator last, const T& value) {
          while (first != last && *first != value) ++first;
          return first;
      }

First and last are declared to be of type InputIterator, and InputIterator is a template parameter. That is, there isn't actually any type called InputIterator: when you call find, the compiler substitutes the actual type of the arguments for the formal type parameters InputIterator and T. If the first two arguments to find are of type int* and the third is of type int, then it is as if you had called the following function.

      int* find(int* first, int* last, const int& value) {
          while (first != last && *first != value) ++first;
          return first;
      }

Concepts and Modeling

One very important question to ask about any template function, not just about STL algorithms, is what the set of types is that may correctly be substituted for the formal template parameters. The basic answer, then, is that find implicitly defines a set of requirements on types, and that it may be instantiated with any type that satisfies those requirements.

Concepts are not a part of the C++ language;  Nevertheless, concepts are an extremely important part of the STL. Using concepts makes it possible to write programs that cleanly separate interface from implementation: the author of find only has to consider the interface specified by the concept Input Iterator, rather than the implementation of every possible type that conforms to that concept. Similarly, if you want to use find, you need only to ensure that the arguments you pass to it are models of Input Iterator. This is the reason why find and reverse can be used with lists, vectors, C arrays, and many other types: programming in terms of concepts, rather than in terms of specific types, makes it possible to reuse software components and to combine components together.

 

Container

A Container is an object that stores other objects (its elements), and that has methods for accessing its elements. In particular, every type that is a model of Container has an associated iterator type that can be used to iterate through the Container's elements.

There is no guarantee that the elements of a Container are stored in any definite order; the order might, in fact, be different upon each iteration through the Container. Nor is there a guarantee that more than one iterator into a Container may be active at any one time. (Specific types of Containers, such as Forward Container, do provide such guarantees.)

A Container "owns" its elements: the lifetime of an element stored in a container cannot exceed that of the Container itself.

 Sequence

A Sequence is a variable-sized Container whose elements are arranged in a strict linear order. It supports insertion and removal of elements.

Associative Container

An Associative Container is a variable-sized Container that supports efficient retrieval of elements (values) based on keys. It supports insertion and removal of elements, but differs from a Sequence in that it does not provide a mechanism for inserting an element at a specific position.

Iterators

Iterators are a generalization of pointers: they are objects that point to other objects. As the name suggests, iterators are often used to iterate over a range of objects: if an iterator points to one element in a range, then it is possible to increment it so that it points to the next element.

Iterators are central to generic programming because they are an interface between containers and algorithms: algorithms typically take iterators as arguments, so a container need only provide a way to access its elements using iterators. This makes it possible to write a generic algorithm that operates on many different kinds of containers, even containers as different as a vector and a doubly linked list.

The STL defines several different concepts related to iterators, several predefined iterators, and a collection of types and functions for manipulating iterators.

Function objects

A Function Object, or Functor (the two terms are synonymous) is simply any object that can be called as if it is a function. An ordinary function is a function object, and so is a function pointer; more generally, so is an object of a class that defines operator().

Carpicorn:here is the overview about the STL. The next section is the studying from Container Classes. 

发表于 2009-4-18 17:00:29 | 显示全部楼层
STL 真的很好用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2025-5-14 19:11

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表