第一章 程序漫谈 

1.1 硬件、软件、程序

1.2 计算机语言

1.3 语言和实现语言的工具

  1.3.1 机器语言

  1.3.2 汇编语言

  1.3.3 高级语言

  1.3.4 语言实现工具

    1.3.4.1 C++ Builder的基本功能

    1.3.4.2 VCL vs. MFC

 

1.1 硬件、软件、程序

 

对于计算机,也许你是老鸟,也许你是菜鸟……

 

但不管怎样,如果你此时此地你要学习编程,那么你应该多多少少知道点什么叫硬件什么叫软件——反正我不管你懂不懂,为了面子,我很不乐意你问我什么叫硬件什么叫软件——我做在这凳子半个小时了,一直想不出如何给二者下个定义。
美国一个电脑神童说:“凡是摔到地上会坏的就是硬件。”我深感不妥,众所周知,如果把硬盘摔到地上,那么硬盘坏了,里头的那些数据——都是软件——也一样地坏得让你我心疼。

 

倘若按字面上理解,那就更加的矛盾重重:硬盘硬是硬件;软盘软也是硬件。
还一种说法是:看得见摸得着的为硬件,看不见摸不着的为软件。刚觉得它说得不错,但马上我就发觉了它的破绽:我现在用的Word2000,它就在屏幕上,界面美观,操作方便……

 

无奈之下,我搬出金山词霸,它说:“硬件:计算机及其它直接参与数据运算或信息交流的物理设备”。挺好。硬件就是设备。平常我们生活中的各种设备,洗衣机,冰箱,电视,还有螺丝刀,钳子,都是硬件。

 

软件呢?“软件:控制计算机硬件功能及其运行的指令、例行程序和符号语言”。指令、程序和符号语言是什么且不说,至少我们得知:软件是用来控制硬件的运行的。

 

这就好办点。我们可以打比方:譬如汽车,其本身自然是硬件,但关于驾驶车的那一套技术,及有关交通规则,我们可称为软件,因为后者控制了前者的运行方式。

 

(一般不传之秘笈:如果你英语一般,学习编程时,别忘装上金山词霸。并且装上后立即上网升级词霸)

现在来谈“指令、程序、和符号语言”。我想交通方面的“软件”确实就是这些东西。我不会驾车,但曾多次看到警察在我上班坐的班车前用指头一指,就令我们的司机脸色发青。之后,一套既定的处罚程序被执行。很快,听说我们的司机又在学习那些用来表示“单行”、“只许右拐”、“不许停车”、“禁鸣”等奇奇怪怪的符号语言了……

 

事实上,说软件看不见摸不着其实也正确。因为它们是思想,精神,规则,逻辑。本身是抽象的,确实不可触及。但软件总是要有载体来存放,要有表达或表现方式,这些使得它们变得形象具体起来。在此意义上,说软件是摔在地上坏不了的东西,也相当行得通,神童毕竟是神童。

 

最后,什么是程序?我决定斗胆来给它下个定义:

程序是一组按照一定的逻辑的进行组合的指令。

 

因此,在以后的学习过程上,很多时候,我们会觉得程序就是指令;同样很多时候,我们会觉得程序就是逻辑。

 

当然,更多的时候,我们并不区分程序和软件二者。也许前者更趋于抽象,而后者趋于具体。比如我们在写那些表达我们的思想逻辑时,我们喜欢说“写程序”;而当程序完成,可以待价而沽时,我们称它为软件产品。

 

1.2 计算机语言

程序用计算机语言写成。编程的实质就是你用计算机语言来表达你解决问题的逻辑。

 

那么,什么叫计算机语言?

 

先不必去解释。因为,计算机是机器,机器不是生物,它怎么能有语言?小猫小狗有语言我尚可相信,机器也有语言,还要我们去学习,这似乎有渎人类之尊严。

如果我不把这个结解开,可能部分特别在意人类尊严的学生对学习编程从此产生心理障碍,无法继续学习……

 

狭义上,我们讲的语言,汉语英语广东话,它是语言,有声音。小鸟之间吱吱喳喳,大抵也是语言。但其实语言二字虽都带口,却不是说非得有声才称为语言:哑语无声,但它也是语言。广义上讲,语言是沟通、交流的一种手段。基于此,我们认为所有的机器或工具,也就包括计算机,都有它们自己的语言。比如锤子,它的语言是敲打;比如螺丝刀,它的语言是拧,如果你非要拧锤子,非要敲打螺丝刀,那么结果就像你用法语和广东佬交谈,用粤语和法国佬说话一样莫名其妙。

 

一般地,越复杂的机器,人类与其沟通的语言也越复杂。譬如汽车,你想驾驭它,你就必须去驾校参加学习。想一想,开车的时候,我们的确是在和车进行沟通。如果你俩之间的沟通出现差错——你心里右转,手却一个劲向左转方向盘,向机器发出了错误的命令——这将多么可怕!

 

至此,我们的心理障碍可以消除了。小猫小狗有语言是因为它们聪明,而机器有语言却是因为它们的笨:它们笨,没办法像动物一样可以通过培训来理会人类的意愿,所以,让人类来反过来为它们定一套沟通的规则,然后人自己去学会这些语言,从而可以方便控制机器。

 

再所以,我们推论,凡是机器语言都是笨笨的语言。机器语言可以分低级语言和高级语言,但无论何者,都是笨得可爱——学得越多你就会越发现它的笨和可爱。另外,当我说越复杂的机器,其语言也越复杂时,我用“一般地”加以修饰。这是因为,发明和发展机器的智者们会为机器制造出越来越高级的语言,这些高级语言,最终越来越接近人类的自然语言。就像计算机,我们有信心相信,终有一天,它能听懂我们的语言——这就是流传在程序员中的一个梦。当程序员熬红眼敲打数万行代码时,他们便会想起这个梦:闭上双眼,伸腰,对PC说:“BEGIN……”;深呼吸一次,然后说:“END”。张眼时发现计算机已完成了所有工作……

 

(一般不传之秘笈:程序员必备之工具:日产乐敦牌眼药水一瓶,用于预防角膜炎;韩产777牌指甲刀一枚,用于铰除因击键刺激而疯长的指甲;国产肛泰若干,治疗因长期坐姿不当而导致的痔疮)

 

回到计算机。它是机器,也是人类有史以来,继发明使用火、电、电子这些改善人类生活的工具后,最为重要,最为先进,最为广泛使用的工具。它的机器语言之复杂程度可想而知,已经复杂到必须成为一门大学的专业课程。然而别忘了我们前面的结论,语言只是沟通的手段。在这个意义上,当你用鼠标或键盘在计算机上进行输入时,只要你输入的是正确操作,我们都认为你在使用计算机语言,因为你确实是在用一种特定的方式或动作,进行和计算机的交流。

 

当然,这里的课程并不特意教你任何有关计算机的基本操作。计算机的基本操作主要是指如何使用计算机内已有的软件产品,比如Windows本身(操作系统是软件,称为系统软件);比如办公系统MS Office或WPS Office(这些实现工作生活中具体应用需求的称为应用软件);比如游戏(一种特定的,只拿来玩的应用,称为游戏软件)。但我们不同,我们学的是如何编写软件。也就是说,我们将是发明人,设计师,创造者;而他们(到今天仍拒不学习编程的家伙)都只是使用者。(我突然有些不安:这么说其实纯属煽情。公平地讲,任何人的任凭创造都是别人的劳动成果之上,任何人也都在创造自己的杰作)

 

程序(或软件)是用计算机语言写出来的。

  • 写一个程序,大致是这么一个过程:
  • 人有一个问题或需求,想用计算机解决……
  • 人想出解决问题或实现需求的思路……
  • 人将思路抽象成数学方法和逻辑表达或某种流程的模式……
  • 程序员将数学方法,逻辑表达中的数据和流程用计算机语言表达,称为代码……

 用计算机高级语言写成的代码被语言的实现工具(VC,VB,Delphi,或C++ Builder)转换成计算机的最低级机器语言。这就完成了人与机器在程序制定上的最后沟通。

 

可见,你的思路是先用人类自己的语言思考,然后用一门计算机语言写成代码,最终,需要一个语言工具来将它转换成机器可以理解的机器语言。我们要学的就是一门承上启下的计算机语言。这样语言有很多:BASIC,Pascal ,C, C++, Java, C#……我们学C和C++。它是使用最多的语言。有关C,C++的更多特点,我们将在下一节谈到。

 

尽管你完全可以直接用最低级的计算机语言——机器语言——来写代码,那样就不需要语言工具了,但在这里你要弄清了,我们不是教机器语言。下一节,你会明白用机器直接能懂的语言——不妨称之为原始的机器语言——写软件,在今天是多么的不现实。

 

1.3 语言和实现语言的工具

1.3.1 机器语言

你知道香蕉叫什么吗?就叫香蕉?叫banana?

 

错,都错。

 

香蕉叫“牙牙”。

 

这是一个baby的语言,一个婴儿还没学会人类的主要语言,所以面对喜欢的东西总是发出咿咿呀呀的声音,也许你听不懂,但这是她的语言。符合小孩特点的语言。

 

计算机的机器语言也一样,必须符合计算机的硬件特点。而痛苦就在这里,越符合机器的特点,同时也就越不符合人类的特点。

 

计算机,全称电子计算机,20世纪40年代,无线电技术和无线电工业的的发展为电子计算机的研制准备了物质基础。1943年~1946年美国宾夕法尼亚大学研制的电子数字积分和计算机ENIAC(Electroic Numerical Integrator And Computer)是世界上第一台电子计算机。ENIAC计算机共用了18000多个电子管,15000个继电器,占地170 m2 ……

 

这是计算机的始祖,一堆电子管。随后,电子计算机进入第二时期,小巧的晶体管取代了电子管;再后,集成电路又取代晶体管,电子计算机进入第三时期。

 

但无论是哪一时期(以后也许不是),计算机始终采用电子器件作为其基本器件,因此,电子器件的特点,就是计算机的特点。

 

为什么使要电子?为什么木头不能做计算机——还真别说不能,您也应该知道,最早出现的用于计算的机器,真是木头的。你用过计算尺吗?算了,这玩意儿太简单。以前有人用木头作成齿轮,经过设计,当表示个位数的齿轮转动一圈时,就会带动表示十位数上的齿轮转动1格。以此原理,只要你转动转轴,木头机器就会算出123+456 = 579……

 

电子元件没有齿轮,但它们的特点是它们有两种很稳定的状态:导电或不导电,假如用不通电时表示0,通电时表示1,再通过集成电路实现进位的机制。于是,计数功能就有了基础。我们用图表示:

 

我们生活中常用的数逢十进一,称为10进制。而计算机,由于其电子元件的特点,它是二进制数。这里简单地对比一下这两种进制造成的区别,以帮助你更容易看明白上图。

 

十进制数:最低位称为个位,高一位称为十位,再高一位称为百位。为什么这样称呼?因为在个位上,0表示0,1表示1,2表示2,3表示3……;在十位上,0表示0,1表示10,2表示20,3表示30……总之,每高一位长十倍,为十进制。

 

二进制数:最低位仍可称为个位,但这里称为1位。1位上,0表示0,1表示1,2呢?没有2,因为逢2就得进1(后面同)。高一位称为2位,0表示0,1表示2,再高一位称为4位,0表示0,1表示4。可以看出,每高一位长2倍,为二进制。

现在看上面的图,00,01,10,是三个二进制数。根据上面的进位方法,你可以算出它位分别表示十进制数的0,1,2来吗?如果你算得出来,不错,值得表扬。算不出来,别急,我来告诉你。首先,当你面对二进制数时,先要扳过来它们从低到高(从右到左)的位依次不再是个位十位百位,而是:1位,2位和4位。

00:都是0,所以它就是0;

 

01:2位为0,1位为1,表示0个2和1个1,所以是1;

 

10:2位为1,1位为0,表示1个2和0个1,所以是2。

 

计算机的机器语言正是由这些0和1组成。事实上,计算机里的所有数据,无论是一个程序,一篇文稿,一张照片,一首MP3,最终都是0和1。

 

世界就是这样奇妙。万事万物五彩缤纷,但进了计算机,却只是个0和1的组合。不由得你会想起道教的古老玄机:“无极生太极,太极生两仪,两仪生四象,四象生八卦,八卦生十六爻”

 

严重跑题。

 

机器语言尽是0和1,于是我们可以想像当时(还没有其它语言时)的程序员是如何编写程序的。他们写程序不用坐在计算机前,而在家里或什么地方,拿笔在纸上画圈,一圈两圈三圈(感觉有点象阿Q?),圈够了就给专门的打孔小姐照着在纸带上打成孔。最后这些纸带被计算机“吃”进去并读懂,然后执行。
来看一眼侏罗纪的程序吧:

 

 

(如用孔表示1,则左图表示三行数110,011,101)

 

面对这样的“程序”你是否表示狐疑?别以为我瞎说,也许你的电脑很先进,是P4吧?但在你的电脑上,仍有那种程序历史遗迹:软驱是也。如果有软驱,那你应该能找到一张软盘。知道软盘有写保护吗?仔细看看那个写保护的开关——就是一个方孔——打开,告诉软驱本张软盘不能进行写操作,关闭,告诉软驱本张软盘可以进行写操作。

 

 

 

 

 

1.3.2 汇编语言

前面说机器语言尽是0和1,那么是不是可以随便写一串0和1就算是程序呢?不是。就像汉语是由汉字组成,可我要是说下面这一串汉字:

 

天爱我京门北安

 

你觉得我是在说人话吗?

 

机器也有自己固定词汇,在机器语言里,称为机器指令,程序是由指令及数据组成。这些指令是一些固定的0和1的组合(不同产商不同型号的机器,其指令又有不同)。作为程序员,就得将这些指令一次次正确地用0和1拼写出来。

 

你决不会将“我爱北京天安门”说成上面的话,但你极有可能将 10101101 写成 10010101,对不?所以很自然地,出现了用符号来表示这些固定的二进制指令的语言,这就是汇编语言。

 

下面是一段我从C++ Builder的CPU调试窗口摘出的代码,它实现的功能是:

 

已知b 等于 1; c等于2; 然后计算 b + c 值,并将该值赋给 a 。

 

把这段代码的机器语言(左)和汇编语言(右)进行对照,你可看出二者各自特点。


10001010 01010101 11000100 mov edx,[ebp-0x3c]

 

00000011 01010101 11000000 add edx,[ebp-0x40]

 

10001001 01010101 11001000 mov [ebp-0x38],edx


汇编语言仅是机器语言的一种助记符,没有本质的区别,所以很多时候,我们把二者等同视之。

 

无论是机器还是汇编语言,都让人看了头痛,好在我们并不去学它们。

 

1.3.3 高级语言

汇编语言和机器语言虽然很难记难写,但它们的代码效率高,占用内存少,这相当符合当时计算机的存储器昂贵,处理器功能有限等硬件特点。

 

众所周知,计算机的硬件发展飞速,功能越来越强大。一方面,它有能力,人们也要求它能处理越来越复杂或庞大数据量的计算功能,机器/汇编语言已经无法胜任实现这些需求;另一方面,硬件的发展和关键元件价格的降低,使得程序员不需要在程序的降低内存占用,运算时间上花太多的精力, 这样,各门高级语言便接二连三地出现了。

 

那么,高级语言“高级”在何处呢?前面我们说过,一门计算机语言“越符合机器的特点,同时也就越不符合人类的特点” 。人类总是希望凡事能舒服点就舒服点,于是某一天,先知先觉们一声怒吼“是该到让计算机语言接近人类的时候了!”从此冒出了Pascal,冒出了C, C++, BASIC语言等数百种高级语言,现在又有Java, C#等等。高级语言高级在哪里呢?就高级在它总是尽量接近“高级动物”的自然语言和思维方式。

 

那么多高级语言,我们为什么挑了C++呢?

 

向来头痛这种问题,其实无论是Pascal, BASIC,还是C++,甚至C和C++相比,每一种语言都有极大的相通之处,又都有各自的独到之处。我大致鼓吹一下C++吧。]

 

首先,这是全世界用得最多的计算机程序语言。

 

其次,C/C++语言既有高级语言的优点,又在很多方面保留了低级语言速度快,可进行很多具有可直接映射硬件结构的操作的长处,我们无时不用的Windows等操作系统,就是用C和汇编写成。事实上很多人称它为中级语言,这样两头俱备的语言,当然值得学。

 

再次,C语言本身,只有32个关键字(固定词),C++也只是进行了有限的扩展。另外,C/C++语言是众多语言中最简洁,紧凑,灵活的语言,学得易,用得爽!

 

再再次,由于前面所说的,C/C++是中级语言,它所生成的机器代码自然更接近于直接用汇编所写(和其它语言相比),所以,同样的程序,用C/C++写,代码小却跑得快。

 

再再再次,C/C++语言是少见的,不专属于(因为版权或龚断)某一家公司或组织,你完全不必担心你只是学了某门某户的C或C++,到别的地儿全玩不转。C/C++有美国国家标准协会制定语言标准。事实上,你就是到UNIX,Linux上写点C/C++代码,也能跑。

 

最后,一门语言再好,如果没有其实现工具(语言本身只是一种规范,必须有编译器可以实现它),那么又不成了屠龙之技,C/C++,你可能用微软的VC,也可以用我们推荐的Borland C++ Builder,二者都相当优秀。

 

1.3.4 语言实现工具

1.3.4.1 C++ Builder的基本功能

我们用高级语言写程序,我们很得意,因为高级语言比较接近人类的语言,我们用起来得心应手,所以我们当然得意。但我们更得意的一定是让程序代码赶快变成可执行文件。

 

无论是在写代码的过程,还是最后要编译成可执行文件,都需要有一个工具存在。这一具一般成为编程集成环境(IDE)。之所以称为集成,是因为从写代码到最后软件的出炉,我们需要它的地方实在太多了。这里列出其中最重要的功能项。

 

1、 代码编辑 方便的代码编辑功能。尽管你可以使用记事本、Word或其它任何文本编辑器来写代码,但除非特殊需要,否则那将是极为低效的方法。相反,现在的编程集成环境,都相当的智能,举例如代码自动功能,可以很多情况下自动完成我们所需的代码,既准确还迅速。Borland公司出品的编程集成环境不仅有常见的关键字高亮等功能,还支持代码模板,支持键盘宏,同样支持高级的脚本插件功能。

 

2、 界面设计 可视化的程序界面设计功能。你所要产生的窗口,在设计期间就真实地出现,包括字体,颜色,定位,比如,你不仅可以插入falsh的动画,而且无需运行,就直接可以在你的界面上看到该动画的演播,这是别的编程环境不能做到的。

 

3、 程序编译 这更是编程工具的主要功能。前面已讲过,我们写的代码,在成为机器能懂的可执行程序时,必须通过编译。

 

4、 程序调试 如何尽量减少你的程序的BUG?没有编程集成环境都提供的强大调试功能,我们做的程序将毫无质量保证。

 

5、 代码优化 Borland 提供的编译器不仅在编译速度一直在美国屡获大奖,而且其代码自动优化功能一直领先对手几近一个时代。使用编程集成环境,我们可以轻松获得更快更优的最终可执行程序文件。

 

6、 辅助程序安装 程序的安装已属于另外一种工具的范畴,但我们仍可以通过编程集成环境来决定最终生成单一可执行文件,还是带有其它动态库。如果是后者,我们还可以通过集成环境来检查程序运行时调用了哪些动态库文件。
C++ Builder 提供的功能远不止我上面所说的,并不是因为我嘴笨,而是我认为对一个工具,你只有动手使用,才会真正了解它。

 

C++是一门语言,而Borland C++ Builder 则是语言实现工具。作为一个编程工具,CB提供以上功能正是份内之事。在这个意义上,你可以认为CB是Word2000,而C++则是英语或汉语。正如我们用英语或汉语在Word2000上写出优美文章,编程可说为:我们用C++语言在CB上编写出优美的程序。

1.3.4.2 VCL vs. MFC 

 

在作为一种编程工具的意义上,我们认为C++ Builder和你也许常听的VC (Visual C++)没有什么本质的区别。就像Word2000和WPS2000在本质都是字处理软件。但现在我们要从另外一个角度讨论C++ Builder这个编程工具。

 

这个角度,就是“封装”——面向对象编程思想中的最重要也是最基础的概念。

 

一个要学习编程的人,可能从C开始学起。学C时,我们没有接触那些挺玄的概念,到了C++,一切就来了,什么面向对象,什么封装、继承、多态……于是我们兴奋起来,努力去理解,掌握,运用这些概念所代表的技术,在掌握这些别人暂时未理能的概念之后而颇有成就感……。现在我要问的是,为什么要有这些概念?这些技术?正确回答这个问题,不仅有助于我们今后对编程语言各种概念的学习,而且它能让我们避免成为新技术的奴隶——这一切也许听起来有些形而上,不过我想通过以下讨论,至少可以回答一个很现实的问题:为什么要选C++ Builder?而不是我们更常听的VC?这是我碰到的编程初学者较疑惑的问题之一。

 

如果人类长有翅膀,那么飞机大抵永远不会被发明。飞机的发明,是为了弥补人类自已不能飞翔的缺陷。不能说所有的技术都是这样,但C++对于C的发展,完全是为弥补程序员脑力的不足。一个在校生在学会C后,往往并没有机会用C去实践一个大中型的项目,体会不到在一个庞大软件工程中,非面向对象语言的短处,所以在之后学习C++的过程中,也就很难真正体会到面向对象语言的长处。简短一点说:不知道C的短处,就不懂C++的长处。相反,倒是很快就发现C++的缺点:它的代码效率多数情况下都要比C低不少。

 

前面我们说过低级语言与高级语言的对比。C++语言也正是从语法结构,语言功能上来限定或实现一门编程语言更加接近人在现实生活中的思维习惯,从而达到减轻人的记忆和判断上的负担。这其中最佳的方法之一就是所谓的“封装”。

 

关于封装,初学阶段最直观的比喻就是抽屉。抽屉将各种对象分门别类地进行存储。譬如中药房,上千种的中药被上千个贴有标签的抽屉“封装”起来,这一充满艺术性的“封装”,使一个一点不懂中药的人也可以去当抓药师,相反,如果没有这些“封装”呢?

 

上一段中我们把对象两字设为黑体,你应该在理解封装的同时明白什么叫对象了:对象就是东西,就是物品,就是事物。不信你把上段中的“对象”替换为“东西”或“物品”,或“事物”是不是读得更顺点?总而言之,面向对象的编程方法其实就是说,让编程的思路尽量符合人们生活中事务处理中已经掌握的科学方法。它并不是牛顿发现地心引力一样的科学探索成果,它只是把已有的科学运用到计算机编程。

 

“封装”中草药的方法也许只有一种,而程序所要解决的问题,比区分中草药要复杂得多,所以,如何进行常用数据,行为的封装,也就仁者见仁智者见智。

 

VC的MFC和CB的VCL都是基于(但不限于)对Windows API(应用程序接口函数)的封装。为什么要对API进行封装?这就是回到了我们前面说过的,为什么有了C又会有C++的问题。因为操作系统是用C和汇编写成的,它获得到操作系统必须的代码效率,但对应用程序开发者而言,它失去了易用性。所以微软和Borland都使用高级语言对之进行封装工作。二者谁进行得更好呢?

 

VC的封装类库称为MFC,它是一种很低阶的封装,它并没有按照人类的思维习惯来得新组织重新解释Windows对象(指Windows编程中所需的数据,处理,机制,接口), 纯粹是API一对一的翻版。这个的封装工作带来代码封装所固有的代码效率降低的副作用,却没有给使用者带来任何方便。如果你是编程初学者,而你身边又有VC高手,那么你一定要多多向他学习请教,因为一个真正的VC编程高手,其同时一定也是一个深刻理解Windows内核机制(消息循环,内存管理,多任务实现,资源使用等),熟悉Windows各种常用API函数等等的高手。相反,如何一个人对这一些知之不多,而自称为VC高手。你放心,参加了笔者下一级的Windows编程学习,只需2星期,你就会明白那种只会用VC的向导写程序的人是什么样的“高手”了 。

 

C++ Builder 对封装库称为VCL(带VC字样,可别以为它是Visual C++,其实它是:Visual Component Library,即:可视控件库)。

 

要想成为Windows编程高手,最终一定要绕过各种封装,理解Windows对象。但作为一个初学者,我们必须挑选一个好的封装。下面我们举字体(Font)作为例子,将三者:没有封装过的Windows 字体API、封装过的MFC字体对象、封装过的VCL字体对象。做一个对比。为了保证不会有偏倚和差错,有关前二者的代码,都是笔者从MSDN(微软提供的帮助文档)中直接拷贝出来。

 
Window API  Windows API创建指定样式字体:

HFONT CreateFont(

  int nHeight, // height of font 

  int nWidth, // average character width 

  int nEscapement, // angle of escapement 

  int nOrientation, // base-line orientation angle 

  int fnWeight, // font weight 

  DWORD fdwItalic, // italic attribute option 

  DWORD fdwUnderline, // underline attribute option 

  DWORD fdwStrikeOut, // strikeout attribute option 

  DWORD fdwCharSet, // character set identifier 

  DWORD fdwOutputPrecision, // output precision 

  DWORD fdwClipPrecision, // clipping precision 

  DWORD fdwQuality, // output quality 

  DWORD fdwPitchAndFamily, // pitch and family 

  LPCTSTR lpszFace // typeface name

  );

MFC (Visual C++) 将HFONT封装为CFont

BOOL CFont::CreateFont (

  int nHeight,

  int nWidth,

  int nEscapement,

  int nOrientation,

  int nWeight,

  BYTE bItalic,

  BYTE bUnderline,

  BYTE cStrikeOut,

  BYTE nCharSet, 

  BYTE nOutPrecision, 

  BYTE nClipPrecision, 

  BYTE nQuality, 

  BYTE nPitchAndFamily,

  LPCTSTR lpszFacename 

  );

VCL (C++ Builder) 将HFONT封装为TFont1

要设置字体名,高度,尺寸等使用以下代码:

  Font->Name = “宋体”; //设置为宋体

  Font->Size = 24; //设置尺寸为24号2

 

将字体的粗,斜,下划线,删除线再封装为TFontStyle属性:

  Font->Style = Font->Style << fsBold << fsUnderlien; //字体增加粗体和下划线属性。

 

对于字体不常用的旋转等属性,不进行封装,你可以直接调用API函数来设置TFont的 Handle属性。

 

比较表中第一行和第二行:前者是原始的API,后者是VC精心的封装成果。可惜二者几近雷同。既然你要封装,你就是要让它变得面向对象,易记易用;一模一样的照抄一遍,然后改改参数的名字,意义何在?如你是想维持代码的效率,那么在繁杂度一样的情况下,为什么我不直接使用效率更高的API函数呢?

 

倘若说,MFC的“封装”纯粹是一种多余,那或许也还可以接受。然而MFC偏偏还要在这种冗余的封装上建立自已的应用程序架构。和前面的“封装”一样,MFC建立应用架构的出发点也是良好的,为了方便Windows程序员编程的难度。结果却更糟糕。有问题的架构犯了类库或接口提供者的大忌:“有协议编程”。

 

什么叫“有协议编程”?我们先来讲“无协议编程”。所谓“无协议编程”是指接口的提供者在提供接口时,同时也提供接口的使用约定。这一套约定应该在接口所要提供的功能上广泛适用,而无须再有种种特殊的例外。这样的接口显然非常适于使用。打个比方就如交通规则。“红灯停绿灯”行应该是普遍适用的规则,如果在红灯停绿灯行还附加以下例外条款:

1、 单号并且在阴天下东行的路面上红灯行绿灯停;

 

2、 轿车在乘客人数为奇数的情况下见红灯允许直行但不能右拐;

 

3、 日间12点到14点区间并且街心交警为女性时,红灯停或行临时取决于女警的身高是否高于1米70……

 

尽管这些例外都是一些不常见的情况,但想像一下你自已是这种制度下生存的司机吧。学习编程,如果挑错了我们每天都要面对的封装类库。就将永远都在努力处理这些无任何意义的问题。和司机不同的是你倒不会有多难受——一个人陷在泥潭中久了,往往就会认为自已挣扎的动作优美得堪称艺术。

 

MFC的CWnd 提供了对Windows最基本的窗口元素的封装,其中对创建窗口的函数的封装为:

 

未封装的API:

 

HWND CreateWindow(

LPCTSTR lpClassName, // registered class name

LPCTSTR lpWindowName, // window name

DWORD dwStyle, // window style

int x, // horizontal position of window

int y, // vertical position of window

int nWidth, // window width

int nHeight, // window height

HWND hWndParent, // handle to parent or owner window

HMENU hMenu, // menu handle or child identifier

HINSTANCE hInstance, // handle to application instance

LPVOID lpParam // window-creation data

);

使用这个API函数,我们可以创建各种窗口。

 

CWnd封装的函数:

 

virtual BOOL CWnd::Create (
     LPCTSTR lpszClassName, 
     LPCTSTR lpszWindowName, 
     DWORD dwStyle, 
     const RECT& rect, 
     CWnd* pParentWnd, 
     UINT nID, 
     CCreateContext* pContext = NULL
     );

 

不用我说,你也能看出这仍然是个改改参数的蹩脚的封装。我们不去管它,现在我们关心的是:CWnd::Create 对 CreateWindow 进行了封装,可是这一封装的结果是:原来CreateWindow能实现的一些事情,在 CWnd::Create里突然成了例外。是的,为了适应CWnd在MFC架构中所处的角色,程序员在涉及CWnd时必须记忆这样一条例外:

“CWnd的Create用于创建窗口的实际元素,但其中参数dwStyle不能包含有窗口风格中WS_POPUP(弹出式窗口),如果你要建立一个带有该风格的窗口,请使用CreateEx……”

……

我仍然要说VC也是一个很优秀的编程工具,但对于不想浪费无谓精力的编程初学者,我个人建议使用Borland C++ Builder,因为它实现真正的对象封装,从而,你可以节省不低于80%的时间来学习编程本质的知识——就是我们常说的数据结构与算法,这些东西最终决定你的编程能力。

这就是C++ Builder提供我们的最重要的东西:

VCL类库:一个好的底层类库,让我们从学习编程最初时刻就自然而然地学会使用面向对象的方法来写程序。它大大降低了我们入门门槛的高度,却又让我们一开始就站在比别人高的位置看待有程序有关的问题;

组件技术:组件技术代表了当今编程技术的主要方向,其设计思想与MS力推的Active 控件如出一辙,拥有相同的先进性。只有借助组件技术,我们才有可能从一个初学者,迅速到达可实际工作的编程工作者;另一方面,如果作为组件的提供者,我们可以编写组件的过程中迅速提高自已的编程能力。

 

C++ Builder还提供了许多其它先进技术,如事件委拖等等,归根到底都通过封装让Windows编程原本需要长期积累的才有可能掌握的知识变得直观易懂。如果你刚刚开始学习编程,或者学习较长时间仍没有重大突破。或许使用C++ Builder结合本课程系列,是个不错的选择。

 

(附言:微软最近推出的C#相信会对上述MFC的不足做一个收拾,它对C++的扩展与约束与Borland C++ Builder 对C++的扩展与约束惊人的相似。如果你乐意,我也真的很建议你在学完C++ Builder后,继续学习C#)

[到页首]