下载第 17章 用 C++建立 ASP组件我们已看到用 C O M服务器组件对于建立一个基于网络的应用程序的重要性,但问题不在于是否建立它们,而在于用什么语言去创建。一种选择是用 C + +。
人们对 C + +有许多不同的看法,保守的 C + +程序员坚持用其他语言创建 C O M组件,他们认为只有真正的程序员使用 C + +。另一方面,V B程序员认为 C + +是一种很难掌握和使用的语言,没有必要增加编程时间和进行艰难的尝试。 J a v a程序员认为他们比 C + +程序员强,因为
James Gosling(Java的发明者 )吸收了许多语言 (包括 C + + )的优点发明了 J a v a,本章和下一章的目的就是消除对 C + +的偏见和错误概念。
本章集中介绍用 C + + 建立服务器组件,不讲述 C + +语言,如果想学 C + +,请参阅
I v o r,H o r t o n著的,Beginning Visual C++6》,Wrox 出版,书号为 ISBN 1-861000-88-X。
本章的主要内容有:
C++简史。
使用 C + +原因。
从 V B移植到 C + +。
AT L,S T L和 M F C介绍。
建立一个 C O M组件。
错误处理与调试。
更重要的是应该记住,现在,不只是在用 C + +建立组件,可以使用 Visual C++中可用的任何工具,使得建立过程更加容易。先从 C + +的起源谈起。
17.1 C++语言在决定是否使用 C + +语言之前,最好是搞清楚这种语言的实质,让我们看一下 C + +的历史和现状。
17.1.1 C++简史刚开始形成的是 C语言,那些想建立更快更有效的代码的程序员非常欣赏 C语言,有一位名叫 Bjarne Stroustrup的人却不满足于仅仅是生产快速代码,他想创建面向对象的 C语言编程。
他开始对 C语言的内核进行必要的修改,使其能满足面向对象模型的要求。 C + +从此产生。
Bjarne Stroustrup是 C + +的最初设计者和实现者。它自诞生以来,经过开发和扩充已成一种完全成熟的编程语言。现在 C + +已由 A N S I,B S I,D I N、其他几个国家标准机构和 I S O定为标准。 I S O标准于 1 9 9 7年 11月 4日经投票正式通过。
C + +标准演变了许多年。 C + +模板是近几年来对此语言的一种扩展,模板是根据类型参数来产生函数和类的机制,有时也称模板为“参数化的类型” 。使用模板,可以设计一个对许多类型的数据进行操作的类,而不需要为每个类型的数据建立一个单独的类。标准模板库
(Standard Tempalte Library,S T L )和微软的活动模板库 (Active Tempalte Library,AT L )都基于这个 C + +语言扩展。
C + +标准可分为两部分,C + +语言本身和 C + +标准库。 C + +标准库对于 Visual C++是相当新的,实际上微软只是在发布 Visual C++ 5.0时去除了一些,b u g” 。标准库提供了标准的输入/输出、字符串、容器 (如矢量、列表和映射等 )、非数值运算 (如排序、搜索和合并等 )和对数值计算的支持。应该说,C/ C + +包含了相对少的关键字,而且很多最有用的函数都来源于库,C + +标准库实现容器和算法的部分就是 S T L。
S T L是数据结构和算法的一个框架,数据结构包括矢量、列表和映射等,算法包括这些数据结构的查找、拷贝和排序等。 1 9 9 4年 7月,A N S I/ ISO C++标准委员会投票决定接受 S T L为
C + +标准库的一部分,这个建议是根据 Alex Stepanov,Meng Lee和 David Musser这三人的编程和软件库研究提出的。 S T L的产生是为了满足通用性的设计目标,而不是为了提高性能。
那么微软对 C + +标准的态度怎什么样?微软运行 V C + +与 Plum-Hall C++,想比较得到的分数在 9 2%和 9 3%之间。为什么不是 1 0 0%的一个原因是跟踪这个标准并同时建立一个编译器比较困难,微软也考虑了对现有编码兼容的重要性,有时他们不得不偏离标准以保持这个兼容性。
17.1.2 使用 C + +的原因应该有充分的理由使用 C + +创建服务器组件,而不只是为了给上司一个好印象才使用 C + +。
如果以前没用过 C + +,你必须要尽力学习。
1,性能性能有个两方面,算法速度和机器代码效率。一个算法可以定义为数据通过系统的概念化的路径,它描述一些点,在这些点上,数据能够被操作并可转换产生某个结果。例如,一个算法定义为获取一个字符串,计算字符串中的字符个数,并作为结果返回的过程。算法与语言是独立的,所以在编程之前必须设计算法,编写一个快速程序的第一个步骤是设计良好的算法,能以最少的操作步骤得出问题的答案。第二个步是选择语言,这也影响程序的速度。
从性能的角度考虑,用汇编语言编写程序是最佳的选择,它是计算机能理解的自然语言。
但是,几乎没有人用汇编语言编写完整的程序,因为这样做极其乏味。另一个最佳的选择是 C
语言。然而,由 V C + +提供的所有工具都产生 C + +,而不是 C。使用 V C + +的向导可以生成大量的使用代码,而不必人工地编写代码。从编写程序的难易程度和程序的性能综合考虑,C + +
是最佳的选择。
C + +性能良好,因为它被编译为机器代码。对于 VB Script和 J a v a等语言,代码在运行时由程序解释,而且每次运行程序时都要将代码转换为机器码,这样做效率比较低,不仅仅是已编译过的 C + +程序运行得较快,而且微软 C + +编译器已存在多年。这意味着微软的编译器程序员已经把许多优点集中到编译器上,以致于它能产生非常高效的机器码。因为 C + +是编译语言,而且非常自然,比 V B更接近机器代码,所以由 C + +编译器产生的代码一定比 V B的编译代码效率更高。
2,错误处理一个好的程序与一个伟大的程序的区别就是其是否具有良好的错误处理支持。实际上,
如果在实现中首先进行错误处理,而不是在最后才进行,那么整个程序的开发和测试过程会更加完美。但是,错误处理只能与语言所支持的内容相一致。
516计计 ASP 3 高级编程 下载
V B S c r i p t具有基本的错误处理支持功能。在默认情况下,不能捕获 V B S c r i p t中的错误。每次怀疑产生错误时,要调用 On Error Resume Next功能,并检查 E r r o r对象。
而 C + +中的错误处理比较好,这是因为有“异常处理”,本章的后面部分将详细介绍。
3,最小的依赖性正如上面所说,C + +是一种编译语言,即 C + +代码在执行之前已转换为机器码。只要此代码不依赖于外部的动态链接库 ( D L L ),C + +就可以在不需要安装额外程序的情况下移动到运行同样操作系统的其他机器和微处理器上,而移动 J a v a程序时需要先安装 J a v a运行期库。
4,利用现有的代码由于 C和 C + +已经存在许多年了,现在有许多可利用的代码,你的服务器组件可以使用现有的 C/ C + +代码或库。例如统计库和到老系统的 C接口。
5,最大化 C O M特征
C O M与 C + +很接近,实际上,Don Box(COM的权威 )在他的,Essential COM》一书的第一章写道:,C O M就是更好的 C + +” 。他说明了 C O M规范是如何从 C + +语言规律中产生出来的。
通过理解 C + +,会对 C O M有更深的理解。
某些语言不能利用所有的 C O M特征,而在 C + +中,几乎可以使用所有的 C O M特征。
17.1.3 不使用 C + +的原因知道什么时候使用 C + +是重要的,同样,知道什么时候不使用 C + +也是重要的。想像一下那些长期维护代码的人,如果他们中没有一些 C + +程序员支持 C + +,那么开发者们不得不把眼光转向另外一些他们熟悉的语言。
改变 C + +组件时,为了看到这些改变的结果,必须重新编译该组件代码,这会花费很长的开发时间。 C + +不能像 A S P页面代码那样,只使用记事本,改变代码的一行,重新装载而得到结果。因此,如果某些工作需要经常变化 (如原型 ),不要用 C + +。
在 C + +中,对一些致命的错误不能获得更多的保护,写一个使组件崩溃的代码是很容易的。
这是为了提供快速代码而付出的代价,C + +不会停下来去检查代码是否按设计运行能否使程序不崩溃依赖于开发者的技巧。如果在这方面花的时间较少或刚刚学习 C + +,最好不要使用
C + +。等到已经意识到 C + +中所有容易犯的错误,而且在检测组件之前花了许多时间,才可以使用 C + +,如果想很快、很容易地建立一个组件,而且也不考虑该组件的执行速度,那么使用 V B吧!
17.1.4 把 A S P技巧转到 C + +上学习新东西的最好方法就是利用现有的技巧。对于 A S P开发者来说,已经学习了 C + +所要求的许多技巧,特别是,J S c r i p t语法和 A c t i v e X或 C O M的面向对象编程的概念。
1,JScript
大部分 A S P开发者都用 J S c r i p t在浏览器上使用 D H T M L。 J S c r i p t的语法与 C非常相似,所以,如果懂得 J S c r i p t,那么就懂得基本的 C语法。当然,只是 C + +语法的子集。 C + +有许多额外的语法来支持面向对象编程,这就是我们下一步要做的。
2,面向对象编程如果你在 V B中使用过类 ( c l a s s ),则对任何 C O M对象和文档对象模型 (Document Object
第 1 7章 用 C + +建立 A S P组件 计计 517下载
M o d e l,D O M )都应熟悉,因为已经有了面向对象编程 ( O O P )的概念。在前面已经说过,C和
C + +的区别是 C + +支持面向对象编程。
17.2 VC++库大量的程序员都尽可能多地利用现有的代码。程序员经常购买那些包装成库的代码,而且许多成功的公司正是靠生产真正优秀的代码库而发展起来的,例如 Rogue Wa v e
S o f t w a r e ( w w w,r o g u e w a v e,c o m )。
当 C语言流行时,代码库就是函数库。例如,可以购买一个数学库,该库含有完成微积分和代数运算的函数。通常,可以在程序代码中使用一个包含文件来指定一个函数库,可以静态或动态地链接这个函数库。
静态链接意味者库代码直接集成到程序中。在这种情况下,程序不依赖于其他文件,但文件的大小可能很大。动态链接意味着程序有库的版本信息,其代码存放于一个单独的文件中,这个文件称为动态链接库 ( D L L )。只有程序运行期间调用 D L L中的函数时,才加载 D L L到内存。 D L L作为一个独立的实体存在于内存中,可以同时被多个程序访问。
出现 C + +后,函数库转变为类库。两者的区别在于函数库只包含一系列函数,而类库是用面向对象编程的原理设计的,例如,可以为数据结构做一个类库,该库包括一个链接列表的类。如果使用一个函数库代替,那么链接列表会独立于操作它的函数。另一方面,使用类库时,链接列表和操作它的函数存在于同一个文件的同一个类中。然而,正如使用函数库一样,
使用类库涉及到包含文件和链接一个静态库。
最近,已经从类库发展到模板库。其原因是 C + +编译器现在已经能够处理 C + +模板。模板库提供了一系列优于类库的优点。要使用模板库,只要在程序中加入包含文件即可,不用链接到库,因为所有的库代码已经包含在该包含文件中了。
这听起来不是非常高效,早期确实如此,因为每个包含有模板文件的文件都要得到一个单独的代码拷贝。因此,编译后的程序会很大。但是,为了适应模板,编译器已经优化,使得这种方法非常高效。
V C有三个供开发者使用的库:一个类库 ( M F C ),二个模板库 ( AT L和 S T L )。
17.2.1 微软基础类库微软基础类库 ( M F C )是微软为帮助 C + +开发者而建立的类库,含有支持各种不同功能的许多类,包括实现复杂的数据结构、操作 Wi n d o w s和访问数据库等。但是,是否应在服务器组件中使用 M F C?回答是否定的!
这很明智的,不要在服务器组件中使用 M F C,服务器组件不能访问 Win 3.2,所以不需要
M F C库,数据库支持最好用 AT L实现。
AT L支持 A D O和 OLE DB,而 M F C不支持。 S T L与 M F C相比有更好的数据结构支持。即使最常用的 M F C类,如 C S t r i n g,在标准 C库中也有等价的类。偶尔会使用 M F C类,如 C S o c k e t,
但它也可被一种 Win 32 API代替。
M F C是用来建立单独 Wi n d o w s应用程序的,不是用于轻量的 C O M组件。只有在与现有的使用 M F C的代码接口时应使用 M F C类。其他情况使用 AT L和 S T L类,这样可以使组件更小且运行得更快。
518计计 ASP 3 高级编程 下载
17.2.2 活动模板库你可以判断出活动模板库 ( AT L )比 M F C新,因为 AT L中有,A c t i v e”这个词,另一种叫法是“微软模板库” (Microsoft Templete Library,M T L )。
AT L用来快速、简易地创建 C O M组件,而且只占用较少的内存,组件的关键部分是由
C + +模板完成的。实际上,AT L的设计在模板的使用上沿用了 S T L。
C + +模板提供一种类型安全的方法来实现一般的接口。很幸运,微软提供了一些产生 AT L
代码的向导,所以在建立简单组件时,不必了解模板,甚至不用知道 A c t i v e X的所有细节。
AT L另一个优点是不依靠 D L L,就是说在装载应用程序时,不必装载 D L L和静态地链接,这两项会增加对内存空间的需求。
AT L使 C O M的开发更加容易,AT L同样支持通过 OLE DB消费者和提供者的数据访问,还有一些特殊的用途,如支持建立微软管理控制台 ( M M C )插件组件。
与 AT L比较,M F C依赖于额外的运行期 D L L或静态链接,所以产生了比 AT L大而慢的代码。
因此,在建立 C O M组件时,如果没有用户界面,那么选择 AT L比 M F C好。
17.2.3 标准模板库标准模板库 ( S T L )对于 V C + +编程员来说是使用得最不充分的有用工具之一,但它是唯一一个标准的和跨平台的 V C + +库。
使用 S T L不多的原因之一是,微软的 S T L文献难以理解。现在没有为 V C + +程序员而编写的 S T L书藉,由 M u s s e r和 S a u n i编写的,STL Tutorial and Reference Guide》
是一本关于 S T L的好书。
S T L对 C + +组件开发很有用,原因是包含了许多数据结构和算法。很多情况下,服务器组件的用途就是处理数据,S T L非常合适这一用途。
微软最初实现 S T L标准是在 VC++ 4.2中,但是,这个 S T L实现做得很差,以致不能使用。
一些人通过使用 H P或 S G I提供的 S T L实现解决这个问题。既然 S T L是一种 I S O/ A N S I标准,它就是跨平台的,只要编译器支持 S T L所要求的结构就可以。然而,微软更新的 S T L实现 (在
VC++ 5.0和 6,0中的提供 ),比以前的 S T L更稳定、更容易使用。
S T L是对 AT L的完美补充,AT L没有数据结构和算法。而且,S T L和 AT L有相似的实现方法,即使用 C + +模板,所以配合得很好,而且两者都不需要链接额外的动态或静态链接库,
这样就会使组件更小,使它们对 D L L的依赖更小。
下面让我们对 S T L的关键概念做些介绍,在建立服务器组件时要用到这些概念。
1,STL遍历器使用 C编程时,一般使用两种方法来遍历数据结构:使用一个下标或递增一个指针,这两种方法达到相同的效果。但是编写一个通用的算法来处理不同的类型是不可能的,用 S T L遍历器能解决这个问题,下面的例子是最好的说明。
下面的程序展示了两种标准 C的遍历方法,最后是 S T L的遍历方法。该程序的任务是计算一个字符串中,t”的个数。
第 1 7章 用 C + +建立 A S P组件 计计 519下载使用遍历器解决了使用指针或下标时出现的问题。指针和下标依赖于数据,这些数据在内存中相邻存放,而遍历器隐藏数据,使数据结构的使用者不必了解数据存储的顺序。隐藏数据结构允许创建通用的算法,这种算法能够与各种数据结构一起工作,例如与容器一起工作。
为了算法的效率,S T L定义了五种遍历器的类:输入、输出、前向、双向和随机访问。算法针对特定的遍历方法设计,因此具有最优的性能。
遍历器相似于指针,移动到下一条目和前一条目分别使用操作符,+ +”和,- -”,同样,
获得一个条目的值使用操作符,*” 。
2,STL映射一个映射就是一个排好序的容器,这里关心的是能够根据存储在条目中的键以尽可能快的速度检索条目,而不是关心使条目以线性的顺序存储在容器中。
S T L排序容器有 s e t,m u l t i s e t,m a p和 m u l t i m a p。 s e t用于条目集合,m a p用于一个条目与另一条目的关联。由于使用双向遍历器,排序容器支持以线性顺序移动数据条目,尽管排序容器的目的是用于键访问。
一个映射容器支持基于单独键的快速数据检索,这个键在映射中是唯一的,映射对于稀疏数据具有节省大量存储空间和减少许多计算时间的潜力,在后面的叙述中你将看到。键和值是以 p a i r类型存储的,这个类型有两个成员,f i r s t和 s e c o n d。检索 f i r s t返回键,检索 s e c o n d返回键的值。映射的遍历器就是 p a i r类型的遍历器。
在本章后面将看到使用映射的例子。
3,STL矢量一个矢量就是序列容器的一个例子,其他序列容器是 d e q u e和 l i s t。它们具有相似的功能,
基本的区别在于性能。例如,除了在 d e q u e的开始处插入和删除的速度比在 v e c t o r中快之外,
一个 d e q u e与一个 v e c t o r没什么区别。不同的问题要用不同的方式来访问数据。矢量能对不同长度的数据提供快速随机的访问,在此序列的末尾插入和删除时较快,而在其他在地方则比较慢。
520计计 ASP 3 高级编程 下载
4,STL算法
S T L算法根据对其操作的数据结构的影响方式和相互作用进行分类。 S T L算法与其操作的
S T L数据结构的接口是 S T L遍历器。 S T L遍历器扮演了一个重要角色,因为它限制了算法怎样访问数据。许多算法接收一个函数作为其接口的一部分,这个函数的参数返回一个布尔值,
表示一种情况。用函数参数可以定制算法。
一些 S T L算法允许函数作为参数传递。多数情况下,这些函数完成一个比较,并且返回布尔值。例如,一个排序算法使用一个函数作为参数来比较两个值,这个函数根据某些准则比较一个值是否比另一个值小。不同的函数有不同的准则,从而导致数据以不同的顺序排列。
更有趣的是,将一个函数对象 (而不是函数 )作为传递参数。函数对象可以包含状态,这种状态可以启用存在于某处的比较规则。使用的比较规则依赖于对象的状态。在本章的后面有一个排序算法的比较函数的例子,以这种方式实现。
5,STL算法的分类
S T L算法可分为四大类。非变异序列算法,操作不改变数据结构内容;变异序列算法则改变数据结构内容;相关排序算法能用于排序、合并和二进制查找等多种算法。最后,S T L具有广义的数值算法,用于计算数据结构中的元素。
非变异序列算法:不直接改变其操作的数据结构的元素。通常,它们查找数据结构中的元素,检查序列元素的等式,计算序列元素的个数。 f i n d和 c o u n t是非变异序列算法的例子,f i n d算法在数据结构的某个范围中移动,查找第一个等于给定值的遍历器。而 c o u n t
算法计算数据结构中等于给定值的元素的个数。
变异序列算法:修改其操作的数据结构中的元素。这些算法在容器中拷贝、替换、转换、
删除和循环移动元素。 f i l l算法是这种算法的一个例子,它的功能是把给定值的拷贝填充到一个序列范围中的所有位置。
相关排序算法:排序、合并和查找元素。同样,能够对排好序的序列进行设置操作,
m e rg e算法是一个例子,它将两个已排序范围内的元素放到一个范围内,并且结果不发生重叠。
广义数值算法的例子是 a c c u m u l a t e,这个算法对数据结构内的某一指定范围的值求和。
6,使用算法的遍历器前面已经讲过,S T L定义了五种遍历器的类:输入、输出、前向、双向和随机访问,算法针对特定的遍历方法设计,以确保优化性能。
例如,f i n d算法可以用于在各种数据结构中查找值,算法并不关心容器是一个矢量,还是一个列表,还是一个数组。唯一的条件是数据结构必须支持 I n p u t I t e r a t o r,I n p u t I t e r a t o r的设计确保它支持的操作的工作效率。 f i n d算法只需要 I n p u t I t e r a t o r支持的操作,因此,f i n d算法能高效地工作。
一些算法要求功能更强的遍历器:如 s o r t需要随机访问遍历器 ( R a n d o m A c c e s s I t e r a t o r )。
提供随机访问是以降低性能为代价的,并影响其他操作,例如在容器中间插入元素的操作。
现在已经你有充分的理论知识,下面建立一个组件。
17.3 建立一个 C + +服务器组件
C + +是标准化的计算机语言,不属于任何人,而属于一个标准委员会。 S T L是支持数据结第 1 7章 用 C + +建立 A S P组件 计计 521下载构和算法的 C + +扩展。 AT L是微软拥有和维护的模板库,使得 C O M编程更容易。综合这些技术形成了创建 C O M组件的一种有效方法,这些 C O M组件用于 A S P页面。
下面用所有这些技术创建一个 C O M对象,你将看到 VC++ 6.0的向导如何提供大量代码,
因此,可以把注意力集中在解决问题上,而不是担心具体的编程细节。
17.3.1 问题表现数据的最普通方法是表,列代表字段的类型,每一行是一条记录,拥有字段的值。
在文本文件中,表通常由用逗号分开的值 (comma-separated values,C S V )组成。
我们将要创建的 C O M组件以 C S V数据作为输入,高效地存储它,并提供访问函数去检索它。这些数据在 C O M组件中以 S T L数据结构表示。在以后部分中,我们会看到怎样用 S T L算法去处理这些数据。另外,在下一章,将介绍怎样在数据库中存储存这些数据。
为了便于说明,假设数据在一个稀疏表中。第一行的字段是列标题,接下来的是一条条数据记录,记录的每个字段对齐于列标题。逗号隔离字段,换行符 ( / n )隔离行,空的字段用两个逗号表示,即“,,,。
表 1 7 - 1是一个展开的表的例子。导出时,逗号会隔离每一个字段。
表 17-1 示例表
N a m e G r o u p I n s t r u m e n t
Jim Morrison The Doors Vo c a l s
Keith Moon The Who D r u m s
Jimi Hendrix Guitar & Vo c a l s
Lenny Kravitz E v e r y t h i n g
Robert Plant Led Zeppelin Vo c a l s
17.3.2 设计这个组件的设计目的是使数据的存储空间和访问时间最小。由于数据有可能是稀疏的,
即许多字段是空的,这就有可能使数据的存储空间最小化。可以通过数值 (基于零的索引 )访问数据的行,可以通过字段名访问数据的列。例如,要得到表 1 7 - 1中 Keith Moon的 I n s t r u m e n t,
可以调用 GetField (1,"Instrument")。
完成以上工作的工具是 S T L的 v e c t o r和 m a p数据结构,这些数据结构是容器,就是说,它们是包含其他对象的一个集合的对象。为了访问集合中的对象,使用 S T L遍历器。
17.3.3 实现现在你对这个组件的功能已经有了概念,我们将按下面的步骤实现它:
创建包含组件的 D L L。
创建组件。
增加属性。
增加方法。
1,创建 D L L和一个组件选择 ATL COM AppWi z a r d,创建一个新的 V C + +项目,然后命名为 A S P C o m p o n e n t s。使
522计计 ASP 3 高级编程 下载用默认的服务器类型 ( D L L ),不选中复选框,如图 1 7 - 1所示。
图 17-1 ATL COM AppWi z a r d的界面 1
当完成后,这个向导会自动产生一个组件的外壳。这时没有组件存在,只有 D L L根据
C O M规范所要求的函数存在,可以在 A S P C o m p o n e n t s,c p p文件中看到这些函数,但不用关心文件的细节,也不用改变它。当向 D L L中添加组件时,向导会修改该文件。
现在将组件放入 D L L。在工作区窗口选择 C l a s s Vi e w选项卡,右击 ASPComponents Classes,
选择 New ATL Object。然后再选 Simple Object,并且在 Short Name框中键入 Ta b l e s t o r a g e,如图 1 7 - 2所示。
图 17-2 ATL COM AppWi z a r d的界面 2
除非你打算更深入地研究 AT L,否则在 A t t r i b u t e s选项卡保持默认状态,不必改变任何选项。对所有这些选项的意义的详细描述已超出本书的范围。 A t t r i b u t e s选项卡如图 1 7 - 3所示。
点击 O K后,就有了一个用来工作的对象。
第 1 7章 用 C + +建立 A S P组件 计计 523下载图 17-3 设置 A t t r i b u t e s选项卡到目前为止,V C + +向导已经完成了所有的工作,如果查看 Ta b l e S t o r a g e,h,你会看到向导产生的 AT L代码。在大多数情况下,不必改变这些代码。实际上,可以只依靠这些代码,不用了解 AT L,继续进行编程。然而,需要对默认的设置做一点改变,使得生成的代码能够编译。
为了减小组件的大小,当 A p p Wi z a r d创建一个组件时,自动关闭 C + +的异常处理。因为如果启用这一功能的话,则需要 C运行期库。 S T L使用异常处理,所以需要启用它。在 P r o j e c t
S e t t i n g s对话框的 C / C + +选项卡中,下拉 C a t e g o r y框选择 C++ Language,确保 Settings for框设置为 All Configurations,选择 Enable exception handing复选框,如图 1 7 - 4所示。
图 17-4 启用异常处理如果用 R e l e a s e模式编译可能会得到一个链接错误。当编译一个发行版本的 AT L
项目时,如果异常处理没有关闭,会出现这样的问题。使用异常处理时,需要 C运行期启动代码;但是在默认方式下,R e l e a s e模式的 AT L项目定义了 _ AT L _ M I N _ C RT符号,它拒绝启动代码。为了解决这个问题,在 C/ C + +预处理器定义中删除 _ AT L _
M I N _ C RT。详细资料请看微软基础知识库中的文章 Q 1 6 5 2 5 9。
524计计 ASP 3 高级编程 下载我们增加的第一段代码用于建立 S T L数据结构,把下列代码加到 Ta b l e S t o r a g e,h的开头:
然后增加下列程序段作为 C t a b l e s t o rg e的预定义成员:
这里,声明了两个映射和一个矢量。这个矢量实际是两个映射之一的矢量。使用 C + +的
t y p e d e f命令定义映射和矢量主要是为了使得代码更易读,对数据类型描述得更好。
C O L UM N _ I N D E X _ M A P将一个字符串 (列的名称 )映射到一个索引中。 I N D E X _ F I E L D _
M A P表示数据表中的每一行的数据。由于他可能是一个稀疏行,用一个映射实现是高效的,
空字段不占用任何空间。 I N D E X _ F I E L D _ M A P映射 C O L U M N _ I N D E X _ M A P提供的索引到字段值。最后,R O W _ V E C TO R包含代表行的每一个映射。
现在已说明了内部数据结构,可将它们用于使用属性的外部世界。
2,增加属性我们将增加两个属性:行数属性和列数属性。
在项目工作区选择 C l a s s Vi e w选项卡,右击 I Ta b l e S t o r a g e接口。在菜单中选择 Add Property,按图 1 7 - 5填写对话框。选择 Get Function复选框,而不选择 Put Function复选框,使得它为只读属性。
图 17-5 增加行数属性第 1 7章 用 C + +建立 A S P组件 计计 525下载产生返回这一属性的 g e t _ n u m R o w s方法。这就是说可有产生属性值的逻辑。在这种情况下,可通过调用行矢量的 s i z e ( )方法设置属性:
用同样的方式增加另一属性,它称为 n u m C o l u m n s。添加如下代码:
现在有了获得所存储数据的行数和列数的方法,还需让一些数据输入到组件中,这是下一步要做的工作。
3,增加方法到目前为止,还无法把任何数据输入到内部数据结构中,也无法读取它们。下面增加四个方法完成下列任务:
在数据结构中插入数据。
从数据结构中获取一个字段。
获取列名称。
对列进行排序。
(1) 分析数据第一个方法将获得一个以逗号隔离的字符串,进行分析,再将数据输入数据结构中。
在项目工作区中选择 C l a s s Vi e w选项卡,右击 I Ta b l e S t o r a g e接口。在菜单中选择 A d d
M e t h o d,按图 1 7 - 6填写对话框。
图 17-6 增加 P a r s e C S V方法
526计计 ASP 3 高级编程 下载然后用下列代码填写 P a r s e C S V方法的主体部分:
C S V数据作为一个字符串参数传递,并清除两个 S T L成员变量,删除以前调用这个方法时输入的数据。
要特 别注意 第一 行,因为 它包 含列 的名称,每 一列 的名 称作 为一 个键存 储在
m _ c o l u m n I n d e x M a p中,用映射的当前大小作为索引。当一行处理完后,可以利用列名称映射的索引也就是列的数值索引这一事实。如果被分析的字段有数据,就将其存储在
I N D E X _ F I E L D _ M A P中。
当达到行的末尾时,映射被存入 R O W _ V E C TO R中。
第 1 7章 用 C + +建立 A S P组件 计计 527下载一旦所有行都处理完后,C O M组件中的 C S V数据的存储空间已经最小化,并且由于使用映射可以加快访问速度。因为只存储了有实际值的字段,所以存储空间最小。由于映射在内部组织数据,可快速检索,访问速度很快。
(2) 数据访问现在数据已能高效存储,下一步是能够访问它。
向 I Ta b l e S t o r a g e接口添加一个称为 G e t F i e l d的新方法,按图 1 7 - 7对对话框进行填写。
图 17-7 增加 G e t F i e l d方法给定行数和列名称,这个方法将返回字段值 (如果它存在 )。改变这个方法的主体,如下所示:
528计计 ASP 3 高级编程 下载用于获得与映射的键相对应的值的映射方法是 f i n d。对于 C O L U M N _ I N D E X _ M A P映射,
从 f i n d中返回 pair<wstring,unsigned short>类型的遍历器。如果没有找到键,遍历器具有
m _ c o l u m n I n d e x M a p,e n d ( )的值。如果找到键,返回的遍历器的 s e c o n d成员包含对应值:在这种情况下,它是列名称的索引。这个索引作为 I N D E X _ F I E L D _ M A P映射的键,给定行即可得到字段值。如果找到这个值,通过 [out,retval]参数返回,也就是 f i e l d Va l u e。
下面创建一个得到列名称的方法。在 I Ta b l e S t o r a g e中添加一个称为 G e t C o l u m n N a m e的新方法,按图 1 7 - 8所示填写对话框。
图 17-8 增加 G e t C o l u m n N a m e方法给定一列的索引,这个方法将返回列的名称,用下列代码改变这个方法的主体:
第 1 7章 用 C + +建立 A S P组件 计计 529下载在这个方法中,必须通过映射进行线性查找,因为程序实际是使用键的值而不是键进行检索,遍历 m _ c o l u m n I n d e x M a p映射直到找到索引或者搜索到映射的末端 (即索引未找到 )。注意遍历器的 s e c o n d成员用于比较,这是因为查找的是键的值而不是键。如果找到列索引,列名称 (实际上是键 )通过 [out,retval]参数返回,也就是 c o l u m n N a m e。
(3) 数据排序下面添加一个对数据有实际影响的方法。使用 STL sort算法对行进行排序。
在 Ta b l e S t o r a g e,c p p文件的开头添加下列语句:
然后给 I Ta b l e S t o r a g e增加一个新的方法 S o r t,按图 1 7 - 9对对话框进行填写:
图 17-9 增加 S o r t方法如果 d i r e c t i o n参数为 0,将以降序来排序,否则就以升序来排序。将 S o r t方法的代码改为如下:
530计计 ASP 3 高级编程 下载因为 C Ta b l e S t o r a g e数据结构是一种 S T L数据结构的组合 (一种映射矢量 ),需要提供一种定制的比较函数进行排序。例如,C Ta b l e S t o r a g e,,S o r t允许行根据任何列排序,这种排序可以仅使用一个比较函数,或使用一个函数对象。用于排序的列被传递给函数对象 d o C o m p a r e的构造器。
在 Ta b l e S t o r a g e,c p p文件某处增加如下函数对象,例如放在 i n c l u d e s和 C Ta b l e S t o r a g e类的实现部分之间。
这是 d o C o m p a r e函数对象的声明和实现部分,在排序中用于比较。确实,这个函数看起来可能复杂一点,详细的语法解释可以查 C + +手册,不过它的目的却非常简单。比较各行以便按 m _ d i r e c t i o n参数指定的次序排序。以列号作为构造器中的第一个参数,并存储在一个成员变量 ( m _ c o l u m n )中。这样,当做比较时,函数对象就知道比较的是哪一列。
注意 d o C o m p a r e是由 STL binary_function模板衍生的。 S T L提供 b i n a r y _ f u n c t i o n使得创建比较函数非常方便。
17.3.4 测试这个组件可用于许多地方:比如 V B程序中,A S P文件中甚至于 C + +程序中。下面分析一下在 A S P中的使用方式。
第 1 7章 用 C + +建立 A S P组件 计计 531下载首先创建一个字符串 c s v S t r i n g,它代表数据。用 S e r v e r,C r e a t e O b j e c t创建一个 Ta b l e S t o r a g e
对象,用 P a r s e C S V方法对字符串进行分析。这时数据在内存中。然后根据名字按升序排序。
获得属性是很容易的,可以这样引用它们:
可以通过遍历整个表显示含有这些数据的 H T M L表。注意 On Error Resume Next。通常,
如果程序试图得到一个字段的值,而该字段没有值,就会产生错误。下一章将用更好的方法来解决这个问题。
图 17-10 运行 c o m p o n e n t Te s t,a s p时的界面
532计计 ASP 3 高级编程 下载注意,c o m p o n e n t Te s t,a s p页面的全部代码可以从 Wo r x网站上下载,本章及下一章的 Visual C++项目 A S P C o m p o n e n t s是本书源代码的一部分。
另外,为了得到一个字段的值,必须指定字段名。一个更灵活的界面应允许使用索引来得到字段的值。运行这个页面,浏览器中应该得到如图 1 7 - 1 0所示的显示。
17.3.5 错误处理错误处理的两个主要方面是:
确保能捕捉所有的错误。
提供错误情况的精确描述。
在程序开发过程中尽量早地使用合适的错误处理工具,能明显减少开发和测试时间,这是因为能更快地发现和纠正错误。可以使用 C + +的异常处理来捕捉错误,使用 E r r o r对象向客户端反馈信息。
1,异常处理错误处理能力受程序语言限制。一个典型的错误处理过程包含如下几个方面:
调用一个函数。
检查返回值。
如果成功,程序采用一个代码路径,如果失败采用另一个。
这个过程的问题是导致代码嵌套,很难跟踪。另外,没有办法迫使程序员检测返回值。
他们可能懒得检查返回值,或是忘了。
我们更希望编程环境能替我们捕捉错误并引导代码按预先确定的错误处理路线运行。在
Visual Basic中可以用 On Error Goto <错误处理函数 >做到这点。当错误产生时,程序将跳到指定的错误处理函数处。也可以在 Visual Basic中调用 E r r,R a i s e来标记一个错误。
C + +和 J a v a拥有相同的错误处理概念,叫作异常处理。异常处理允许把代码的一部分放入 t r y块来保护它。如果错误产生于 t r y块,那么程序的执行跳到 c a t c h块,在那儿有错误处理代码。当错误产生于 c a t c h程序块时,代码将自动指向 t r y程序块。使用 t h r o w关键字发出错误信息。
下面是异常处理的简单例子:
函数与下面的代码类似:
第 1 7章 用 C + +建立 A S P组件 计计 533下载在第 1 8章中将使用这种异常处理风格。
一旦捕获了错误,下一步是将错误信息返回给用户。如果所使用的 C O M对象的宿主环境是 A S P,最好的方法是通过 E r r o r对象报告错误。
2,Error对象如果未用 On Error Resume Next,当对没有值的字段调用 G e t F i e l d方法时,将遇到如下错误:
这个提示用处不大,在错误产生时需要组件能提供更多的信息,可以通过 E r r o r对象来做到这一点。
如果创建一个新的 AT L对象,并使用 E r r o r对象,可以选择 ATL Object Wizard Properties对话框中的 Support ISupportErrorInfo复选框,指示向导产生支持 E r r o r对象的代码。
手动插入代码也是很容易的。仅需把包含文件 Ta b l e S t o r a g e,h做如下修改:
534计计 ASP 3 高级编程 下载通过这种修改,我们的类将支持 ISupportErrorInfo COM接口。然而,需要在源文件
Ta b l e S t o r a g e,c p p插入下面的代码,才可以实现增加的方法。
我们的类从 C C o m C o C l a s s派生,有一个 E r r o r方法。也就是说 C Ta b l e S t o r a g e类能够使用
C C o m C o C l a s s定义的所有公共方法和属性。现在 C Ta b l e S t o r a g e类支持错误接口,因而可以使用这个方法。 C Ta b l e S t o r a g e类也提供 E r r o r对象的所有参数,包括错误代码、描述和帮助信息。
下面修改一下 G e t F i e l d方法以便提供更多有用的信息。用下面代码修改方法的结尾。
现在将得到如下的错误信息:
现在不仅得到了增加的错误信息,而且得到了组件的 P r o g I d。当组件支持 I S u p p o r t E r r o r I n f o
时,P r o g I d信息将自动地插入。
另外,我极力推荐程序员在组件开发初期使用错误支持。这将有助于程序员调试组件,
并能够帮助组件用户的开发工作。当然,如果不能从错误信息中判断出是什么出了错,下一步就是调试程序了。
17.3.6 调试在 Visual C++中可以直接调试一个正常的可执行程序。可以设置断点,然后在调试模式下第 1 7章 用 C + +建立 A S P组件 计计 535下载运行程序。如果有 D L L,则需要做更多的工作,C + +调试器必须附加上支持调试的进程。
一个 D L L不在自己的进程运行,它运行在其他进程中。因此,必须给调试器捆绑上能容纳 D L L的应用程序进程空间。如果在 Visual Basic中测试,就需要捆绑上 Visual Basic;如果是在 A S P上测试,则需要捆绑上 We b服务器进程。另外,在 Visual Basic中,可以运行 V B 6,E X E,
打开一个使用 D L L的项目,像正常情况一样设置断点。
还有一个直接设置组件进行调试的方法,只要在组件中需要调试的地方加上下面一行即可:
当容纳 D L L程序的进程运行到这一行时,将停下来弹出一个对话框并告诉你已经到了断点,并询问是否调试应用程序。按 C a n c e l调用调试器 (注意,按 O K不进入调试器 )。如果组件是调试版本而不是发行版本,当按 C a n c e l时,Visual C++将开始运行并停在插入 D e b u g B r e a k ( )
的地方。在这可以设置另外的断点、观察变量和做其他的调试工作。
这种方式的主要问题是其侵入性。为了调试代码必须修改代码,还得十分小心避免遗留
D e b u g B r e a k ( )。如果组件产品中有 D e b u g B r e a k ( ),运行时将弹出对话框并锁住每一个用户使其脱离 We b服务器,等待用户按下 O K或 C a n c e l。这是很糟的。
如果组件在 A S P中使用,修改然后重新编译就会出现如下错误:
LINK,fatal error LNK1168,connot Debug/ASPComponent.dll for writing
Error executing link.exe
服务器组件在 We b服务器的进程空间中运行,如果得到上述错误,意味着服务器仍有此组件的引用。更确切地说,服务器正引用组件中的 D L L,所以需要解除对 D L L的引用。但是,
做到这些需要关闭并重新启动 W W W服务。然而,如果对象是在 M T S中运行,这个过程就简单多了。
C O M +调试首先,确定设定的激活属性 (activation property)是专用的服务器进程而不是库进程。这才能确保调试器绑定到所要调试的组件的进程。在 Project Settings选项卡上,将 Executable for
debug session项设置为 d l l h o s t,e x e,将 Program arg u m e n t s项设置为 P r o c e s s I D,<进程的 I D >,如图 1 7 - 11所示。
图 1 7 - 11 设置激活属性
536计计 ASP 3 高级编程 下载进程的 I D可以通过在 Component Services Explorer中右键单击应用程序图标,并选择
P r o p e r t i e s来获得,如图 1 7 - 1 2所示。
图 17-12 获得进程的 I D的界面关闭组件所在的应用程序的服务器进程,确保组件当前不会驻留在内存中。否则,可能会使用没有绑定调试器的组件实例。确被建立组件的调试版本,在调试版本中设置所需断点并从 B u i l d菜单中执行程序。
运行带有程序参数的 d l l h o s t,e x e,以指示它载入包含被调试组件的应用程序。因为 C O M +
进程绑定了调试器,无论什么时候访问组件,调试器都能在断点处中断调用。
如果对象失败,有关 C + +中的组件的其他情况都自动地放在事件日志中。
17.4 小结本章介绍了 C + +的起源以及设置与开发 A S P组件相关的环境。读者已经学习了一部分 C + +,
如 S T L和 AT L,这些都是创建 A S P组件的有用工具。这些工具都能满足服务器组件的设计需要,
其目的就是减少存储空间和访问时间。为了介绍它们的使用方法,本章创建了一个能用于任何应用程序的通用组件。
组件的可利用能力的一个重要方面是其错误处理。 C + +异常处理提供了在组件中捕获错误的有效方法。要想在应用程序调用之外报告错误,组件需要支持 I S u p p o r t E r r o r I n f o。
下一章将分析在 Visual C++中支持 A S P和 C O M +的集成,也将讨论在 C + +中访问数据的两个选项。
第 1 7章 用 C + +建立 A S P组件 计计 537下载