1、C和C++够笨,逼的程序员自己做很多事
C其实大家都知道,是一门中低级语言,保留了指针等汇编特征,与系统底层结合密切。C++呢,更多的向个Object C,即面向对象的C。但这些,其实并不能够让程序员显得很牛。我个人的理解,之所以C和C++程序员显得很牛,其实原因和大家想像的正好相反,不是因为C和C++太强大,而是这两门语言太弱智了。
C和C++,由于设计时,主要针对系统底层,操作系统层面的应用,因此,其提供的核心基本库相对比较简陋,没有提供很多华丽的框架,VC的MFC不算啊,那是针对Windows的,不是C++的基本特性。这直接导致了C和C++程序员,在进行大型开发时,缺乏现成的框架模板可以使用,也没有太多的工具库可以使用,像Java等其他语言,基本都提供了常见的几乎所有数据结构类,或者工具模块,不过,C和C++没有,搞得C和C++程序员,这群可怜的娃,啥事都喜欢自己做。
我就经常干这种事,实现个队列,实现个树,经常自己从头写起,无形中呢,自己被逼的把很多常见数据结构,弄得滚瓜烂熟,很多时候,小弟问我一个功能如何实现,我说“站着别动,立等可取!”,然后几分钟,十几分钟就实现一个给他,呵呵,这不是我想牛叉,这是被逼出来的。
》》》如何学好C语言
学生大本营很多朋友教训我,说我重复造轮子,弄得我哭笑不得,有那么多现成的轮子用,哪个龟孙想自己造,这不是没办法给逼的嘛,5555。。。
2、C和C++够通用,逼的程序员广种薄收
这个通用,分两层意思。
一个是从应用角度,C和C++是目前我见到唯一的可以适应任何应用场合的语言,它对应用通用。起码,除了汇编语言,C和C++大概是唯一用来写操作系统的语言了,很多底层的驱动程序,操作系统内核级模块,只能用C和C++来开发,因此,一个C和C++程序员,他唯一不知道的,可能就是不知道自己以后会去写什么平台的什么程序,这要求他必须努力学习很多语言之外的知识,为下一份工作做好知识储备。
第二个是从跨平台来说,C和C++又不是很通用,虽然从第一天开始,C就吹嘘自己很牛叉,是跨平台语言,但是,就我个人的经验,可以很负责任地告诉大家,它在吹牛。
没错,作为基本语句,C和C++程序可以到大多数平台的编译器下开发,不过,稍微涉及应用一点的库,差异就很大,比如同样的socket编程,Windows下和Linux下就有很大差异,什么线程库啦,很多,差异都很大。不过,日常工程开发,离了这堆库,程序员估计也不用做什么事了,因此,很多C和C++程序员自从被骗得上了贼船,没办法,就得面对无穷无尽的学习,操作系统,任务调度,内存管理,资源管理,很多都得自己来,去理解各个操作系统的差异性,然后自己写程序来适应。
这么弄下来,搞得C和C++的程序员,无形中对于很多语言之外的知识,掌握得就多一点了。
3、C和C++不是一门应用级的语言,其程序员的要求也不是应用程序员的要求
对于很多语言来说,比如Java,PHP,Python等,在开发之初,都有明确的应用方向,同时,为了适应跨平台需求,都做了一定性能上的牺牲,这类语言,一般比较擅长应对企业级数据库应用,即办公应用系统,以及网站开发等。
应该说,这已经是很大的市场了,足以让一个程序员吃一辈子的饭,因此,学会了这类语言以及应用开发,一般已经可以再80%~90%的开发市场找到饭碗了。
不过,C和C++不一样,C和C++从设计出来,就没有明确的应用方向,或者说,唯一明确的应用方向,就是做操作系统。我们知道,操作系统是适应面最广的应用软件,所以,C和C++就显得很保守,为了通用,它宁愿不提供什么功能过去强大的库,一切靠程序员自己来。
换而言之,做C和C++的程序员,或多或少都有一点操作系统,系统底层相关的知识,这部分知识,一般难学,费解,比起学习应用开发,使用某些框架模板来做某一类型应用来说,C和C++程序员需要更加深入的思考加学习,无形中,造成这类程序员没事喜欢瞎琢磨的特性,我就没事老是喜欢想操作系统内核一些功能,我来实现该怎么做。
这种事情想多了,不用说大家也知道,对自己的水平无形中,有一点点促进,因此,C和C++程序员往往看问题会深入一点,善于思考。
4、C和C++是高性能应用的唯一选择,这造成程序员必须学习优化
我们知道,虽然很多语言都可以写游戏,不过,作为大型游戏系统,比如暗黑破坏神吧,还有很多网游,出于高性能图形动画的需求,必须使用C和C++来实现,才能保证效率。很多大型网站的后台数据库,为了响应高吞吐量,高并发量的客户请求,也需要使用C和C++进行优化。
这就造成了,C和C++程序员一玩就是大的,必须对优化非常熟悉才行。我们知道,优化,有两个要求,一是对各类算法必须很熟悉,可以进行算法比较,另外,也是需求分析的专家,可以根据需求特性,做量身定做的优化。
这就要求C和C++程序员,经常陷入算法比对之类的任务,而且,也不能仅仅做一个程序员,还必须时刻思考客户需求,对需求做不断的再分析,才能找出优化之路。
我们知道,任何事情,做多了,就成熟练工了,自然也就来得快,C和C++程序员就是这个样子。可能还有很多其他理由,不过,我匆忙间,也就想出这几条。不过,大家可以看到,日久天长,C和C++程序员,不管是否自愿,但工作和学习中,确实比别语言的程序员在多想,多看,多学,自然而然,就显得很牛叉了。
但大家注意到没有,其实我上面分析的几点,与C和C++其实没有太多的关系,任何一门语言的程序员,只要坚持上面几点,不断修炼自己,其实都可以达到牛叉的地步。
所谓“功夫在诗外”,又所谓“穷人孩子早当家”,C和C++之所以显得牛人多,并不是这两门语言有多好,其实恰恰相反,是这两门语言没有给程序员提供太多帮助,而其应用环境又太多太广太深,逼的程序员在自我不断的学习,几年,十几年下来,自然显得比小日子过的还算不错的其他语言程序员,要深入一点。
但是,我们还是要看到,每一门语言,都有其存在的价值,起码程序语言排行榜前十的语言,都是伟大的语言,都可以产生大师,我觉得大家完全没有必要人云亦云,都一窝蜂跑来做C和C++,在很多应用市场上,Java、PHP等脚本语言,都以开发周期短,开发时间快,成为优选语言,C和C++完全没有办法和这些语言比较。
计算机软件开发,发展到现在,其实已经是很细分的市场,有专门的应用软件市场,也有系统软件市场,C/C++和Java等,各自应对的是不同的市场,因此,二者其实没有可比性的。
建议已经选择Java、.net等其他语言方向的同学,不要跟风,在自己的领域深入进去,你们也能成为牛人的。
》》》如何学好C++语言
人们不懂拼音也会讲普通话,如果懂得拼音则会把普通话讲得更好。不懂面向对象程序设计也可以用C++编程,如果懂得面向对象程序设计则会把C++程序编得更好。
对象(Object)是类(Class)的一个实例(Instance)。如果将对象比作房子,那么类就是房子的设计图纸。所以面向对象程序设计的重点是类的设计,而不是对象的设计。类可以将数据和函数封装在一起,其中函数表示了类的行为(或称服务)。类提供关键字public、protected和private用于声明哪些数据和函数是公有的、受保护的或者是私有的。
这样可以达到信息隐藏的目的,即让类仅仅公开必须要让外界知道的内容,而隐藏其它一切内容。我们不可以滥用类的封装功能,不要把它当成火锅,什么东西都往里扔。
类的设计是以数据为中心,还是以行为为中心?
主张“以数据为中心”的那一派人关注类的内部数据结构,他们习惯上将private类型的数据写在前面,而将public类型的函数写在后面。
主张“以行为为中心”的那一派人关注类应该提供什么样的服务和接口,他们习惯上将public类型的函数写在前面,而将private类型的数据写在后面。
很多C++教课书主张在设计类时“以数据为中心”。我坚持并且建议读者在设计类时“以行为为中心”,即首先考虑类应该提供什么样的函数。Microsoft公司的COM 规范的核心是接口设计,COM的接口就相当于类的公有函数[Rogerson1999]。在程序设计方面,咱们不要怀疑Microsoft公司的风格。
设计孤立的类是比较容易的,难的是正确设计基类及其派生类。因为有些程序员搞不清楚“继承”(Inheritance)、“组合”(Composition)、“多态”(Polymorphism)这些概念。
继承与组合
如果A 是基类,B是A 的派生类,那么B将继承A 的数据和函数。示例程序如下:
class A
{
public:
void Func1(void);
void Func2(void);
};
class B :public A
{
public:
void Func3(void);
void Func4(void);
};
// Example
main()
{
B b; // B的一个对象
b.Func1();// B 从A 继承了函数Func1
b.Func2();// B 从A 继承了函数Func2
b.Func3();
b.Func4();
}
这个简单的示例程序说明了一个事实:C++的“继承”特性可以提高程序的可复用性。
正因为“继承”太有用、太容易用,才要防止乱用“继承”。我们要给“继承”立一些使用规则:
一、如果类A 和类B毫不相关,不可以为了使B 的功能更多些而让B继承A 的功能。
不要觉得“不吃白不吃”,让一个好端端的健壮青年无缘无故地吃人参补身体。
二、如果类B 有必要使用A的功能,则要分两种情况考虑:
(1)若在逻辑上B是A 的“一种”(a kind of),则允许B 继承A的功能。如男人(Man)是人(Human)的一种,男孩(Boy)是男人的一种。那么类Man可以从类Human派生,类Boy可以从类Man派生。
(2)若在逻辑上A是B 的“一部分”(a part of),则不允许B继承A 的功能,而是要用A和其它东西组合出B。例如眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,不是派生而成。
结合“抽象基类”和“多态”有如下突出优点:
(1)应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。这一
招叫“以不变应万变”,可以大大提高程序的可复用性(这是接口设计的复用,而不是代码实现的复用)。
(2)派生类的功能可以被基类指针引用,这叫向后兼容,可以提高程序的可扩充性和可维护性。以前写的程序可以被将来写的程序调用不足为奇,但是将来写的程序可以被以前写的程序调用那可了不起。
以下是我编程时采用的命名约定:
(1)宏定义用大写字母加下划线表示,如MAX_LENGTH;
(2)函数用大写字母开头的单词组合而成,如SetName,GetName ;
(3)指针变量加前缀p,如*pNode;
(4)BOOL变量加前缀b,如bFlag;
(5)int变量加前缀i,如iWidth;
(6)float变量加前缀f,如fWidth;
(7)double变量加前缀d,如dWidth;
(8)字符串变量加前缀str,如strName;
(9)枚举变量加前缀e,如eDrawMode;
(10)类的成员变量加前缀m_,如m_strName, m_iWidth;
对于int,float,double 型的变量,如果变量名的含义十分明显,则不加前缀,避免烦琐。如用于循环的int型变量i,j,k;float型的三维坐标(x,y,z)等。
程序一般分为Debug版本和Release版本,Debug版本用于内部调试,Release版本发行给用户使用。断言assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。
assert不是一个仓促拼凑起来的宏,为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。所以assert不是函数,而是宏。程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。
很少有比跟踪到程序的断言,却不知道该断言的作用更让人沮丧的事了。你化了很多时间,不是为了排除错误,而只是为了弄清楚这个错误到底是什么。有的时候,程序员偶尔还会设计出有错误的断言。所以如果搞不清楚断言检查的是什么,就很难判断错误是出现在程序中,还是出现在断言中。幸运的是这个问题很好解决,只要加上清晰的注释即可。这本是显而易见的事情,可是很少有程序员这样做。这好比一个人在森林里,看到树上钉着一块“危险”的大牌子。但危险到底是什么?树要倒?有废井?有野兽?除非告诉人们“危险”是什么,否则这个警告牌难以起到积极有效的作用。难以理解的断言常常被程序员忽略,甚至被删除。[Maguire1993]
以下是使用断言的几个原则:
(1)使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。
(2)使用断言对函数的参数进行确认。
(3)在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?”一旦确定了的
假定,就要使用断言对假定进行检查。
(4)一般教科书都鼓励程序员们进行防错性的程序设计,但要记住这种编程风格会隐瞒错误。当进行防错性编程时,如果“不可能发生”的事情的确发生了,则要使用断言进行报警。