1998.09.28第一次发表于清大资讯BBS(枫桥驿站)电脑资讯版 140.114.87.5
每年的09/28对于我来说都是一个特殊的日子——不只是因为教师节。今年很特殊地没有普天同庆,那么我就写篇文章自己庆祝一下好了。
我于今年7月发表了一本著作《多态与虚拟》和一本译作《深度探索C++对象模型》,获得了很大的回应。这些作品都不是针对C++的完全初学者所写,但从低阶到高阶为数众多的C++ guy,热情地表达了他们对这些主题的喜悦。
在许多来信中,我看到一些有趣的现象,也感受到一些值得整理下来的想法。所以,根据我个人的学习过往、我的教学经验、以及周围朋友的心得交流,写下这篇文章,或可成为后学者的戒律。
首先让我节选《多态与虚拟》一书的序言:
一般而言C++是一个难学易用的语言。C++的难学,初始在与其重重的帷幕,帷幕之中编译器对我们的程序代码又做了太多的手脚,使我们惯于循序思考的脑袋一无所措。因此,又面临新的思维模式,使我们必须扭转常规的思考习惯。
C++的易用则在于其巨大的弹性,能够以多态(polymorphism)、虚拟(virtual)、模板(template)、泛型(generalization)等种种形式,让已有的代码去处理未知的、未来的数据类型。
当然,易用必须先能用。用不好或不能用的话,”写C++程序“最后就成了只是”使用C++编译器“,这是大家常拿来彼此调侃的笑话。
在”难学“的背景下,”易用“是使我们依然前仆后继的动力。越来越多的大学信息专业把C++课程开在大一,这虽然说明C++是多么地重要,可也苦了信息专业新兵们。
其实”难学“的最大症结在于,很难有一本书,能够一针见血地指出多态与虚拟的重要性;在我们具有语法基础之后,直接把我们引到到最核心最重要的思想,并且在建立这个思想的过程中,提供足够必要的基础。
”C++是难学易用的语言“,这句话相信很多人心有戚戚。C++的学习难度,一在于语言本身太多的”幕“,一在于“paradigm shift”(思考模式的转移)。
传统顺序语言如C,Pascal,Basic,Fortran……除了模样看起来稍有不同,基本上都是函数call来call去,大同小异,很容易掌握。你想做的动作,在code中都看的一清二楚。你所看不到的,重大的部分也不过就是编译器为你的函数加上用于处理堆叠的小段代码(prologue 和 epilogue),这一小段代码基本上做的是杂物,你没看到也没有关系(更好),并不影响你对程序逻辑的思考。
C++不一样,C++有太多和程序逻辑息息相关的动作是编译器为我们加上去的。换句话说,C++编译器为我们“加代码”。如果不认清这一节,学习C++有如雾里看花,雾非雾,花非花。
编译器为我们C++程序加了什么代码呢?很多!对象诞生时ctor会被调用,对象消亡时dtor会被调用,这些都是加码的结果。ctor中设定vtpr和vtbl,这也是加码的结果。new单一对象时会产生memory block cookie,new对象会产生一个内部结构记录 object size 和 class ctor……这也是幕后工作。可以说,程序代码中看不到而却必须完成的所有程序选择有关的动作,统统都是C++编译器加代码后的结果。
当“继承”发生,整个情况变得稍微复杂起来。“多重继承”有更复杂一些,“虚拟继承”再更复杂一些。
这些幕后的主题,统统可以归类为所谓的C++ object model(对象模型)。如果不知道这些底层机制,你就只能够把make destructors virtual in base classes(Effective C++, item14)或never treat arrays polymorphically(More Effective C++,item 3)这类规则硬背下来,却不明白它的道理。
用一样东西,却不明白它的道理,林语堂如是说:“不高明”。只知道how,不知道why,侯捷如是说:“不高明”。
C++的第二个学习难度在于“paradigm shift”(思考模式的转变)。别说自己设计classes了,光使用别人的classes,就都是一种思考模式和行为模式的转变。MFC或OWL或VCL的使用者必然能够领略并体会我的意思。
使用所谓的application framework(一种大型的、凝聚性强的、有着面向对象的公共基础建设的class library),你的代码和framework之前究竟是怎样的关系呢?framework提供的一大堆可改写的虚拟函数的意义与价值究竟在哪里呢?为什么framework所实际的种种美好性质以及各式各样的算法竟然可以在我们自己的设计的class type身上使用呢?framework被设计时,并不知道我们(使用者)的存在呀!
稍早所说的C++对象模型,偏向于程序设计的低级层面;这里所说的思考模式转变,择时程序设计的高级层面。能够把新思维模式的威力发挥得最淋漓尽致的,当推面向对象的polymorphism(多态)和generalization(泛型)。如果你没有使用这两项特性,等于进入C++宝山而空手返回。
想象C++是一把用来解决程序问题的刀,要它坚韧,要它锋利,就必须多次回火,在高热和骤冷之间淬炼。
初学C++语法(syntax)之后,你应该尽快尝试试验polymorphism(大致而言也就是虚拟函数的运用)。等到对OOP的精神有了大局掌控的能力,但对C++的许多小细节不甚清楚,就是回到C++对象模型淬炼的时机。
成长,就是在高阶(polymorphism)和低阶(object model)之间反复循环,才能够上升到更高的阶层,而不是平平庸庸于中阶(C++ syntax)的一潭死水。
100个人跟我说他懂C++/OOP,只有10%不到可以让我任务他没有吹牛皮。太多的人,上嘛上不到polymorphism,下嘛又下不到object model。就这样不上不下地卡在C++语法层面。大一学了C++,到大四快毕业了,连virtual functions是怎么回事都期期艾艾支支吾吾说不出道理。
有时候我觉得,太苛责同学也于心不忍,因为同学们事实上处于一种无知的状态,既不知道C++/OOP该怎么学,也不知道哪些书可以教他们正确地学。所以,苛责同学,不如苛责老师。
众所周知,大学教授多半是动口不动手,普遍心态是“论文第一,升值为要。程序语言?哎,末流!”“末流”课程通常由我们教授轮流教,谁倒霉谁来教;于是就有“下学期要学C语言了,这学期寒暑假赶快去要本书来恶补”的情况发生。偏偏程序设计语言这东西,只动口不管用,一定要动手,而且要勤动手。如果老师自己没有摸到C++/OOP的精神,学生又能学到什么呢?
有些学校的信息专业并不教特定的编程语言,老师们的态度是“语言是一种自己学就好了的东西嘛,拿到大学殿堂来,哎,不入流”!于是应该为学生打好基础的时间,却拿来天马行空腾云驾雾,大谈抽象意念。饱读经书的老师们可能忽略了,技术底子不够的学生最需要的不是形而上的道,而是形而下的器。
我们是先能够欣赏具象画,还是先能够欣赏抽象画?我们不都是先对着毕加索的画大叹“这是什么东西”,直到自己的艺术涵养够丰富了、人生阅历更饱满了、能够举一反三了、能够触类旁通左右逢源了,才转而开始欣赏或甚至进入毕加索的抽象意境吗?
老师们各有所长,要老师们来教非自己专长的大班课、基础课,我又觉得似乎也太为难了。那么,苛责老师,不如苛责学校当局。如果学校当局能够聘请经验十足又有教学热诚的工程师来教这类实操类课程,不是三方皆大欢喜吗?别说什么制度僵化、难以突破,大学是高度自治,聘请几位兼职老师,不全都是系里的职责范围内吗?
学子们在课程上学不到东西,就只好闭门自学。但是,顺序性(sequential)语言尚有自学学会的可能,面向对象的语言,以大学生的程度来讲,我认为自学实在困难,通常学出四不像、半瓶水。现实所见,比比皆是。
对底层知识有浓厚兴趣的朋友,下探到object model领域,一定会非常开心地在object size、object layout、vptr/vtbl、以及许多幕后的技术之间玩嗨起来。了解这些东西当然是好的,但是由于一探究竟得其奥秘的快感与成就感,使得一些 朋友们在这个层次里面“玩”起来了:小地方玩得很精,玩得不亦乐乎,玩得忽略了C++/OOP的最终目标。
最终目标是polymorphism!
我要说,在C++ syntax以及相对低阶的C++ semantics或甚至更低阶的C++ object model里,不要玩得太过火。过犹不及,会伤身的。C++经典名著的习题,有些在我看来颇有玩得过火的味道。
Programming应该是一种天马行空的想象力与创意的组合;如果你能够自己想题目,譬如实现一个天体运行的class体系,或是做一个生物分类(界门纲目科属种)体系,不是很有趣吗?准备资料的过程中,查查百科全书,你也因此查到了太阳系九大行星的资料,哈雷彗星的轨道周期,或是~的“界门纲目科属种”英文名称,这难道不比钻研那些纯粹头脑体操的题目有趣吗?
固然,在科学与工程的领域里,无技术无以为立,但别把自己弄得过于僵化,过于匠气。僵化与匠气是我们教育体系最大的痛处。到了高级层次,败象显露无遗。
如果没有介绍几本好书,我就是为德不卒。
C++相关书籍,如天上繁星,如过江之卿。广博如四库全书者有之(The C++ Programming Language、C++ Primer),深奥者如重山复水者有之
以下是我认为你应该拥有的书籍。有趣的是,我才在自己的班上做了一个调查(我教的是面向对象的Windows程序设计,学生应该有良好的C++/OO基础),拥有以下/~5本书的人举手。举手人数都很少,而且老是那么几位(最高记录是拥有四本)。这让我感觉,强者恒强,弱者恒弱。悲哀!
-
C++ Primer(3/e),Lippman/A.W./1998
-
The C++ Programming Language (3/e), Bjarne/A.W./1997
这两本书是C++经典百科全书。就内容水平而言,不相上下。普遍的印象是,第一本较易接受,第二本涩味稍重。第二本书作者Bjarne是C++语言的创造者,所以具有其权威性。我认识多位C++/OOP高手,都是两书都有。
-
Inside The C++ Object Model, Lippman/A.W./1996
此书讲解C++ object model,。内容很好,层次也高,可惜原文书大大小小的文字错误繁如星辰,阅读时需要小心。
-
Effective C++, Meyers/A.W./1992
-
More Effective C++, Meyers/A.W./1996
这两本书以条例的方式讲解C++ programming的重要观念,使你的程序更健壮更有效率。书中许多观念涉及C++ object model,与3书混合看,如鱼得水。
-
Polymorphism in C++ 多态与虚拟 侯俊杰/松岗/1998
此书在语法粗略的基础上,直接把读者引导到面向对象最核心最重要的概念,并且在建立这个概念的过程中,提供足够的必要基础。
这里只列出一本中文书,是因为这方面的中文书我看得少,英文书看得多。“恐有遗珠之憾”这类“八方得体”的话,还是说一下好了。
注意,这些都是强本固元来扎实基础的书籍而已,要观摩大型程序经验,还有诸如Large Scale C++ Software Design(John Lakos/A.W./1996)可以阅读。
OO的世界,不止OOP,还有OOA/OOD,那又是一缸子的学问和一缸子的书。