距离我的上一篇文章——写给VC++ Windows开发的初学者已经4年多时间过去了,感慨于时光如梭之余,更感慨于这么多年来(从1998年我初学VC 算起吧)到如今其实我仍然还只是个初学者而已。看看之前写给大家的东西,似乎说了些什么,但仿佛又什么都没有说。好多网友也都慕名加入了我的VC++群,一同探讨学习。
虽然学者众,甚至我的群中也不乏各路高手,更甚至我自己也在这几年努力制作出版了很多网络视频教程来现身说法教学VC++大法,可是事与愿违,VC++现在却越来越小众化了,很多人都转投C#、Java的怀抱了,甚至我认得的很多朋友都转投安卓、iOS开发阵营去了,只留我孑然一身孤军奋战于VC++开发的一线,甚至我还要偶尔冒充下所谓高手,来为大家解答很多在我看来不该是问题的问题。
迷茫之余,我也看到了很多希望,好多网友甚至只是90后,从一工作就加入了VC++开发者的光荣行列,于是乎我在想我应该在为大家写点什么,而不只是为初学者。今天通过这篇文章就把我的一些初学心得再与大家做个分享,为大家照亮一盏明灯,消除大家的一点点淡淡的孤独。也许可能帮不到你什么,但至少可以点燃各位心中那浅浅的共鸣,我也就心满意足了。
回顾多年来自己的经历,主要是学习VC++的经历,我发现一个让我困扰已久的问题的些许答案(或许还不是终极答案),就是为什么VC会那么难学,很多初学者甚至被搞的欲哭无泪,几欲放弃,但又难割难舍。正所谓“花自飘零水自流,一种相思,两处闲愁。此情无计可消除,才下眉头,却上心头。”
其实比之很多其他语言来说C++语言本身已经不能与C相提并论了,虽然很多书宣称学会了C再学C++很容易,但现在看来这是忽悠各位上“C++贼船”的宣传而已。如果你真的这么天真的以为靠学校那点C语言的基础就可以轻松驾驭C++,那么你真的错了。
如今C++语言已经承载了很多时代IT新生语言的特色,比如面向对象,只4个字已让C++扩充了C语言n倍以上的知识点,再比如到现在的C++2011中的伦巴达表达式(使用VS2013你会有惊喜的发现),更是追逐时代的脚步,甚至让我都差点追不上青春的尾巴。C++其实从语言的角度讲,已经是一个全新的紧跟时代脚步,甚至是超前设计的重量级的高级计算机语言。仅学习和掌握这个语言本身,已经可以耗费你n(n>=1)年的时间。我不知该为这些新颖的特性开心,还是悲哀。这些其实都是我们学习和最终应用VC++的一道坎,一个不知不觉你必须通过的坎。但是相比其它语言来说,C++的学习成本因为复杂的特性而有点高了,仅指针一项就可以把很多初学者屠杀于摇篮状态,更弗论精通!
相比较而言,java和C#语言因为大胆的阉割(剔除指针)等因素,虽然语法特性较之C++并不逊色,甚至可以说是超越了C++的语法特征,但是学习的成本反而下降了几个数量级(参透这点让我很感慨,不知你作何感想),因此你学习这些语言时几乎可以一个月入门并上手,而C++能做的就是让你在初学的几个月时间内饱受折磨。当然我不排除哪些天才,天生为C++而生,学习几无困难,如果你是这类天才,也就可以略过此文了。相较而言,当我们还在挣扎于C++的伦巴达表达式与函数指针的区别时,java或C#的程序员已经在琢磨整个程序的架构该如何设计的问题了。差距慢慢的就在不知不觉间产生了。
更甚至因为java、C#本身的设计特征,它们自身就包含了很完备的语言支持库,这些库几乎涵盖了所有操作系统应当具备的基本功能,比如java运行库中就有完整的线程及线程池支持,因此学习它们你不用去管操作系统怎么实现线程的更不用去管操作系统中的所谓线程池特征是如何实现的,你要做的就是懂得基本原理调用即可。而更现代的语言运行时库中更是直接加入了并行计算需要的所谓CAS操作等先进特性支持,它们与各自的语言结合的如此的紧密以至于很多人都已经不用去区分那些是语言本身,那些是语言运行时库或开发支持库了。它们浑然天成!
而作为C++的程序员,或者说作为VC++的程序员你可能就没这么幸运了,你能面对的就是弱的可怜的C/C++运行时库,或者是那个所谓过时了的MFC库,有时你甚至区分不清楚Windows API和C库函数究竟有什么区别?当你正在纠结的时候可能与你同行的java程序员已经在研究设计模式与实际开发相结合的问题了。这就好像你还在纠结于发动机上某个螺丝该如何设计制造时,它们已经开始琢磨用什么样的发动机装配出什么样的汽车与飞机等问题了,甚至已经在制定飞机或汽车的相关设计标准了,显然此时用落后已经不能来形容个中的差距了。
最终导致的残酷现实就是,同样的应用需求,java程序员在考虑用一种开放的SOA体系结构和那种它们称之为spring框架的东西在“设计”这个应用时,你却在考虑这个应用中某个功能应该调用哪个API来实现的微观问题。同样的当java程序员已经搭完这个应用的全部框架,并搞出demo的时候,你可能还在论坛里寻找某个API应该如何调用来实现某个特定功能的问题。强悍的C++如核能一般的巨大能量,却被禁锢在如原子核般狭小的空间中。可以这样更形象的来形容这样的场景:当Java和C#语言的程序员已经点燃了一个崭新的太阳时,而我们还在用C++模拟几个原子核的碰撞,并为那瞬间微不足道的闪光和能量释放欢呼雀跃!
当然上面描述的场景完全是以一种相同时间轴和周期进度来横向比较的,比如同时开始学习java和C++的两个文艺青年,在两年后可能就真的出现了如上的差距。当然如果你是一个学了10多年的老C++,跟一个搞了两年Java的程序员讨论设计模式的话,我不知面对这样的一个场景会是一个什么样的心情。
很多时候,我作为一个旁观者,观看着群里高手解答如我这般初学者的问题时,我发现,很多初学者居然傻傻分不清什么是消息,什么是函数,更被MFC消息映射搞的晕头转向。但却很难见在群中有人讨论一下用什么设计模式,搞定某个设计问题,或者应该如何整体的看待C语言运行时库、STL、Boost、以及Windows API、MFC、ATL、WTL等之间的关系。有些甚至陷入了COM的细节中而无法自拔。
时间久了,我发现其实不是大家不关注这些问题,而是做为一名VC++的程序员,你根本就没时间关注这些问题,或者说语言的细节已经可以填满你所有的时间空隙了,根本就无暇去顾及这些看似宏观也根本无关痛痒的问题。但现在我发现觉得这些东西无关痛痒,不用去细究的话,是真真正正的大错特错了。
比之Java程序员C#程序员用CAS大谈特谈Lock-Free算法如何利用并行提高程序效率这种高大上的话题来说,我更有一种觉得他们就像是想用并行的几张汽车的奔跑速度,来超越C++这枚火箭的飞升速度一样。而遗憾的是很多C++程序员居然不知道Lock-Free,国内资料更是凤毛麟角。在国外的网站上Lock-Free却是先由C++玩出来,然后Java和C#平台才跟进的,国内却是大把Java和C#的NB人物大谈特谈各种Lock-Free算法该如何实现,如何使用,带来如何的性能改善。而鲜见C++于这方面的探讨。这种奇观就好像一群太监大谈特谈什么样的姿势可以带来那种超爽的高潮,而一群正常的男性反倒不知什么是高潮这种情形一样让人匪夷所思。
如此这般,C++必然每况愈下,渐渐的就成了小众。看着那些Java写出来的怪兽占据了动辄几百台甚至n多中型机的机器上,耗费着足够每月买你的一台PC或笔记本的电费,奔跑着Lock-Free这种名义上是优化,其实本质只是为了让这些机器上的CPU都不要闲着的程序,制造着忽悠用户的高性能谎言。让我n多次陷入了沉思。
这一切都是为什么?首先我不是歧视那些Java和C#的程序员,更不是要挑起语言优劣的无意义的争执。我只是在思考为什么C++的程序员不能像他们那样轻松快意的制造出NB的程序呢?难道仅仅是因为所谓的C++语言学习成本太高吗?
现在我试着来说说我这段时间思考的结果:
首先,C++甚至是VC++还有那些各种平台上的C++都在使用着那个C/C++运行时库,这个库的历史比我年纪都要大,而很多教程把这个库当作了C或C++语言的整体,其实这个库在我看来完全就是个鸡肋(不是基类,不要以为我打错了)。这个库功能少的可怜,除了那些操作字符串的并且漏洞百出函数,我真不知道还有什么东西让我可以留恋。也许很多看客已经准备开始因这个开始骂我了,第一条我想可能就要跟我理论用这个库可以实现跨平台,然后末了骂上一句SB(据非正式统计有80%的C++程序员觉得别的C++程序员都是SB,哥是剩下20%中的,哈哈)。在我看来要用C++实现跨平台,需要的是进行抽象设计,而不是全部限定在这个弱到爆的标准库上,而如果你还有什么需要理论的话,还不如思考个问题:就是那些号称可以跨平台的充分利用了C标准库函数的C++第三方开源组件库,为什么几乎都不带界面组件呢?同时为什么好多这类库都是慢的要死的?利用了抽象设计的跨平台库你就不要跟我说了,而是正好证明了哥的观点,比如KlayGE、U3D、cocos2D-X等跨平台引擎,几乎都是靠抽象设计出来的。第二条很多看客可能又要跟我说这个库好多重要的功能,什么静态全局对象初始化,文件操作,等等怎么能说功能少的可怜呢?其实这个问题你真该跳出来看看,比如Java库C#库中的CAS函数族、线程池、标准界面库,标准绘图函数等等高大上的完全各自语言风味的功能在哪里呢?有空随便找本Java的书来看看,光看看目录你就会发现原来咱们的C++库真的少了好多东西。其实我不是来跟你争论这个库好不好的,而是要告诉你,你应该把所有的操作系统平台都看作是C++语言的支撑库,因为几乎所有的操作系统都是用C/C++实现的,甚至包括Android系统和iOS系统。其实现在在我看来,这些操作系统就是为了C、C++的运行封装了一下硬件层而已,所以这是个巨大的宝库,因此你不用纠结该用操作系统的API还是用标准库函数,反正哥是全部用操作系统的API包括操作字符串这样的函数都直接用操作系统的,当然貌似尤里平台(Unix/Linux)下将C运行时库当作了自己不可分割的一部分,因此操作字符串还是用strcat、strstr这样的函数。而对于用VC++的Windows程序员来说,开始用StringCch函数族吧。不习惯裸用函数的话,自个抽象设计一下用这族函数封装个string类出来,不要问我为什么推荐用这套函数,详情请去看本人的《VC++系列之Windows服务器与网络编程》系列视频课程吧。这样一来,你会发现,原来C++的库函数也可以如此的丰富,什么界面、线程池、同步、CAS、甚至高大上的异步IO功能应有尽有,你需要做的就是找到对应的每一组API当作C++函数调用即可。这也是我这久思考以来第一个观点——把操作系统本身看作一个C/C++运行时库,当作C++语言的一部分。而不是将它们看作神秘的操作系统,不关语言任何事。这样一来其实对于跨平台你应该有另一种认识了,比如你调用了Windows IOCP写了个服务器程序,某天老板被某些忽悠高手给搞去让你开发Linux服务程序时,不要慌,Linux上有个epoll模型跟那个IOCP类似,而你需要做的就是两件事情一是搞懂这个epoll模型的原理和API(操作系统的API哦),第二就是完成你的抽象设计兼容epoll和IOCP。最终你会发现,原来操作系统是如此相似啊?每个平台上有的,另一个平台上也有啊,只是原理稍微有点出入,而API彻底不同而已,为什么呢?因为所有操作系统都是硬件的封装层,都是C++语言的运行时库,所以它们必然有着类似的功能。而你要掌握的只是说应该有哪些功能,再去找对应功能的API,其实就是C++库函数调用而已。沿着这个思路,你可以想象一下CreateWindow这个函数在Linux平台下有没有替代物呢?如果这样认为的话,其实我一直再想为什么Windows不能像Linux那样干脆彻底的C/C++风格呢?仅仅是为了支持多种语言环境吗?其实那些语言,甚至像微软自家的VB这样的语言有几个是直接用操作系统的功能或者说直接调用API呢?不都是封装了一遍吗?甚至微软后来干脆为C#把整个Windows都封装成了.Net库。既然如此为什么不干脆把Windows封装成C/C++库,再用它们去封装这些个语言的运行时库呢?搞的现在Windows API因为一种怪异的中性化的设计风格,弄的VC++程序员在学习API时始终是一种即陌生又熟悉的矛盾心理,于是VC++程序员高呼:信春哥,得永生!其实哥只爱女神的。
其次,我建议大家学习完C++语言之后干的第一件事情最好是开始认真学习设计模式。要超越Java、C#、objective-C程序员掌握的程度之上,甚至要能够总结出很多C++味道很浓的设计模式,比如智能指针这类C++才有的设计模式。至于为什么?我想你学懂了自然就明白了。那时你会发现你之前的C++开发是多么的低级,仅仅是为了调用几个API实现某个功能而堆砌出来的玩具。用了设计模式,你就可以像他们(Java、C#、objective-C等程序员)一样考虑宏观的高大上的软件架构之类的问题。设计模式是道,而具体的某个API调用或者C++库函数只是术,以道御术才是正解!
再次,我建议学习VC++的人不要老是陷入一些微观的细节的甚至无意义的问题中去,比如C++语言Java语言C#语言究竟哪个好,这样无聊的只会引发口水大战的问题。而是要多关注IT时代的新方法,新趋势上。比如现在多核CPU已经大行其道的情况下,你却对并行计算一无所知,还天真的以为会多线程编程再加上所谓的线程同步就可以轻松驾驭多个CPU的话,只能说你已经Out了。对于一些所谓过时的技术比如COM这样的东西,可以了解下,但不用陷进去,而是先开阔自己的眼界,如果你眼界宽了,这类东西反倒很容易学习和理解。其实这最终我想说的就是做为一个VC++的程序员,你应该有更宽阔的视野,不要仅仅局限在那么几个有限的领域。或者形象的说STL是C++的一部分不错,但是STL不是C++语言的全部。更可以说,IT世界整个都应该是属于C++的,因此只学会了STL,只学会了指针,你只是才起步而已。因此,我一直觉得我其实也只是个初学者而已!
写到这里收笔吧,如果你看懂了点什么,那么我很欣慰,并最终告诉你:行动吧,骚年!如果你觉得平平常常,只是觉得有几个笑点的话,我会告诉你:大笑吧,骚年!