C++的坑真的多吗?

标签: C/C++语言 杂项资源 编程语言 C++ Java | 发表时间:2012-08-06 08:12 | 作者:陈皓
出处:http://coolshell.cn

先说明一下,我不希望本文变成语言争论贴。希望下面的文章能让我们客观理性地了解C++这个语言。(另,我觉得技术争论不要停留在非黑即白的二元价值观上,这样争论无非就是比谁的嗓门大,比哪一方的观点强,毫无价值。我们应该多看看技术是怎么演进的,怎么取舍的。)

事由

周五的时候,我在我的微博上发了一个贴说了一下一个网友给我发来的C++程序的规范和内存管理写的不是很好(后来我删除了,因为当事人要求),我并非批判,只是想说明其实程序员是需要一些“疫苗”的,并以此想开一个“程序员疫苗的网站”,结果,@简悦云风同学 直接回复到:“ 不要用 C++ 直接用 C , 就没那么多坑了。”就把这个事带入了语言之争。

我又 发了一条微博

@左耳朵耗子  新浪个人认证 : 说C++比C的坑更多的人我可以理解,但理性地思考一下。C语言的坑也不少啊,如果说C语言有90个坑,那么C++就是100个坑(另, 我看很多人都把C语言上的坑也归到了C++上来),但是C++你得到的东西更多,封装,多态,继承扩展,泛型编程,智能指针,……,你得到了500%东西,但却只多了10%的坑,多值啊。

结果引来了更多的回复(只节选了一些言论):

  • @淘宝褚霸 也在微博里说:“自从5年前果断扔掉C++,改用了ansi c后,我的生活质量大大提升,没有各种坑坑我。”
  • @Laruence 在其微博里说: “我确实用不到, C语言灵活运用struct, 可以很好的满足这些需求.//@左耳朵耗子: 封装,继承,多态,模板,智能指针,这也用不到?这也学院派?//@Laruence: 问题是, 这些东西我都用不到… C语言是工程师搞的, C++是学院派搞的”

那么,C++的坑真的多么?我还请大家理性地思考一下

C++真的比C差吗?

我们先来看一个图——《 各种程序员的嘴脏的对比》,从这个图上看,C程序员比C++的程序员在注释中使用fuck的字眼多一倍。这说明了什么? 我个人觉得这说明C程序员没有C++程序员淡定

Google Code 中程序语言出现 fuck 一词的比率

不要太纠结上图,只是轻松一下,我没那么无聊,让我们来看点真正的论据。

相信用过C++的程序员知道,C++的很多特性主要就是解决C语言中的各种不完美和缺陷:(注: C89、C99中许多的改进正是从C++中所引进的

  • 用namespace解决了很C函数重名的问题。
  • 用const/inline/template代替了宏,解决了C语言中宏的各种坑。
  • 用const的类型解决了很多C语言中变量值莫名改变的问题。
  • 用引用代替指针,解决了C语言中指针的各种坑。这个在Java里得到彻底地体现。
  • 用强类型检查和四种转型,解决了C语言中乱转型的各种坑。
  • 用封装(构造,析构,拷贝构造,赋值重载)解决了C语言中各种复制一个结构体(struct)或是一个数据结构(link, hashtable, list, array等)中浅拷贝的内存问题的各种坑。
  • 用封装让你可以在成员变量加入getter/setter,而不会像C一样只有文件级的封装。
  • 用函数重载、函数默认参数,解决了C中扩展一个函数搞出来像func2()之类的ugly的东西。
  • 用继承多态和RTTI解决了C中乱转struct指针和使用函数指针的诸多让代码ugly的问题。
  • 用RAII,智能指针的方式,解决了C语言中因为出现需要释放资源的那些非常ugly的代码的问题。
  • 用OO和GP解决各种C语言中用函数指针,对指针乱转型,及一大砣if-else搞出来的ugly的泛型。
  • 用STL解决了C语言中算法和数据结构的N多种坑。
(注意:上面我没有提重载运算符和异常,前者写出来的代码并不易读和易维护(参看《 恐怖的C++语言》后面的那个示例),坑也多,后者并不成熟(相对于Java的异常),但是我们需要知道try-catch这种方式比传统的不断地判断函数返回值和errno形成的大量的if-else在代码可读性上要好很多)

上述的这些东西填了不知有多少的C语言编程和维护的坑。 少用指针,多用引用,试试autoptr,用用封装,继承,多态和函数重载…… 你面对的坑只会比C少,不会多。

C++的坑有多少?

C++的坑真的不多,如果你能花两到三周的时候读一下《 Effecitve C++》里的那50多个条款,你就知道C++里的坑并不多,而且,有很多条款告诉我们C++是怎么解决C的坑的。然后,你可以读读《 Exceptional C++》和《 More Exceptional C++》,你可以了解一下C++各种问题的解决方法和一些常见的经典错误。

当然,C++在解决了很多C语的坑的同时,也因为OO和泛型又引入了一些坑。消一些,加一些,我个人感觉上总体上只比C多10%左右吧。但是你有了开发速度更快,代码更易读,更易维护的500%的利益。

另外,不可否认的是,C++中的代码出了错误,有时候很难搞,而且似乎用C++的人会觉得C++更容易出错?我觉得主要是下面几个原因:

  • C和C++都没学好,大多数人用C++写C,所以,C的坑和C++的坑合并了。
  • C++太灵活了,想怎么搞就怎么搞,所以,各种不经意地滥用和乱搞。

另外,C++的编译对标准C++的实现各异,支持地也千差万别,所以会有一些比较奇怪的问题,但是如果你一般用用C++的封装,继承,多态,以及namespace,const, refernece,  inline, templete, overloap, autoptr,还有一些OO 模式,并不会出现奇怪的问题。

而对于STL中的各种坑,我觉得是程序员们还对GP(泛型编程)理解得还不够,STL是泛型编程的顶级实践!属于是大师级的作品,一般人很难理解。必需承认STL写出来的代码和编译错误的确相当复杂晦涩,太难懂了。这也是C++的一个诟病。

这和 Linus说的一样 —— “ C++是一门很恐怖的语言,而比它更恐怖的是很多不合格的程序员在使用着它”。注意我飘红了“ 很多不合格的程序员”!

我觉得C++并不适合初级程序员使用,C++只适合高级程序员使用(参看《 21天学好C++》和《 C++学习自信心曲线》),正如《 Why C++》中说的,C++适合那些对开发维护效率和系统性能同时关注的高级程序员使用。

这就好像飞机一样,开飞机很难,开飞机要注意的东西太多太多,对驾驶员的要求很高,但你不能说飞机这个工具很烂,开飞机的坑太多。(注:我这里并不是说C++是飞机,C是汽车,C++和C的差距,比飞机到汽车的差距少太多太多,这里主要是类比,我们对待C++语言的心态!)

C++的初衷

理解C++设计的最佳读本是《 C++演化和设计》,在这本书中Stroustrup说了些事:

1)Stroustrup对C是非常欣赏, 实际上早期C++许多的工作是对于C的强化和净化,并把完全兼容C作为强制性要求。C89、C99中许多的改进正是从C++中所引进。可见,Stroustrup对C语言的贡献非常之大。 今天不管你对C++怎么看,C++的确扩展和进化了C,对C造成了深远的影响

2)Stroustrup对于C的抱怨主要来源于两个方面——在C++兼容C的过程中遇到了不少设计实现上的麻烦;以及守旧的K&R C程序员对Stroustrup的批评。 很多人说C++的恶梦就是要去兼容于C,这并不无道理(Java就干的比C++彻底得多 ,但这并不是Stroustrup考虑的,Stroustrup一边在使尽浑身解数来兼容C,另一方面在拼命地优化C。

3)Stroustrup在书中直接说,C++最大的竞争对手正是C,他的目的就是—— C能做到的,C++也必须做到,而且要做的更好。大家觉得是不是做到了?有多少做到了,有多少还没有做到?

4)对于同时关注的运行效率和开发效率的程序员,Stroustrup多次强调C++的目标是——“ 在保证效率与C语言相当的情况下,加强程序的组织性;能保证同样功能的程序,C++更短小”, 这正是浅封装的核心思想。而不是过渡设计的OO。(参看: 面向对象是个骗局

5)这本书中举了很多例子来回应那些批评C++有运行性能问题的人。C++在其第二个版本中,引入了虚函数机制,这是C++效率最大的瓶颈了,但我个人认为虚函数就是多了一次加法运算,但让我们的代码能有更好的组织,极大增加了程序的阅读和降底了维护成本。(注:Lippman的《 深入探索C++对象模型》也说明了C++不比C的程序在运行性能低。Bruce的《 Think in C++》也说C++和C的性能相差只有5%)

6)这本书中还讲了一些C++的痛苦的取舍,印象最深的就是多重继承,提出,拿掉,再被提出,反复很多次,大家在得与失中不断地辩论和取舍。这个过程让我最大的收获是——a) 对于任何一种设计都有好有坏,都只能偏重一方,b) 完全否定式的批评是不好的心态,好的心态应该是建设性地批评

我对C++的感情

我先说说我学C++的经历。

我毕业时,是直接从C跳过C++学Java的,但是 学Java的时候,不知道为什么Java要设计成这样,只好回头看C++,结果学C++的时候又有很多不懂,又只得回头看C最后发现,C -> C++ -> Java的过程,就是C++填C的坑,Java填C++的坑的过程

注,下面这些东西可以看到Java在填C/C++坑:

  • Java彻底废弃了指针(指针这个东西,绝对让这个社会有几百亿的损失),使用引用。
  • Java用GC解决了C++的各种内存问题的诟病,当然也带来了GC的问题,不过功大于过。
  • Java对异常的支持比C++更严格,让编程更方便了。
  • Java没有像C++那样的template/macro/函数对象/操作符重载,泛型太晦涩,用OO更容易一些。
  • Java改进了C++的构造、析构、拷贝构造、赋值。
  • Java对完全抛弃了C/C++这种面向过程的编程方式,并废弃了多重继承,更OO(如:用接口来代替多重继承)
  • Java比较彻底地解决了C/C++自称多年的跨平台技术。
  • Java的反射机制把这个语言提升了一个高度,在这个上面可以构建各种高级用法。
  • C/C++没有一些比较好的类库,比如UI,线程 ,I/O,字符串处理等。(C++0x补充了一些)
  • 等等……

当然时代还在前进,这个演变的过程还在C#和Go上体现着。不过我学习了C -> C++  -> Java这个填坑演进的过程,让我明白了很多东西:

  • 我明白了OO是怎么一回事,重要的是明白了OO的封装,继承,和多态是怎么实现的。(参看我以前写过的《 C++虚函数表解析》和《 C++对象内存布局》)
  • 我明白了STL的泛型编程和Java的各种花哨的技术是怎么一回事,以及那些很花哨的编程方法和技术。
  • 我明白了C,C++,Java的各中坑,这就好像玩火一样,我知道怎么玩火不会烧身了。

我从这个学习过程中得到的最大的收获不是语言本身,而是各式各样的编程技术和方法,和技术的演进的过程,这比语言本身更重要!( 在这个角度上学习,你看到的不是一个又一个的坑,你看到的是——各式各样让你可以爬得更高的梯子

我对C++的感情有三个过程:先是喜欢地要死,然后是恨地要死,现在的又爱又恨,爱的是这个语言,恨的是很多不合格的人在滥用和凌辱它。

C++的未来

C++语言发展大概可以分为三个阶段( 摘自Wikipedia):

  • 第一阶段从80年代到1995年。这一阶段C++语言基本上是传统类型上的面向对象语言,并且凭借著接近C语言的效率,在工业界使用的开发语言中占据了相当大份额;
  • 第二阶段从1995年到2000年,这一阶段由于标准模板库(STL)和后来的Boost等程式库的出现,泛型程式设计在C++中占据了越来越多的比重性。当然,同时由于Java、C#等语言的出现和硬件价格的大规模下降,C++受到了一定的冲击;
  • 第三阶段从2000年至今,由于以Loki、MPL等程式库为代表的产生式编程和模板元编程的出现,C++出现了发展历史上又一个新的高峰,这些新技术的出现以及和原有技术的融合,使C++已经成为当今主流程式设计语言中最复杂的一员。

在《 Why C++? 王者归来》中说了 ,性能主要就是要省电,省电就是省钱,在数据中心还不明显,在手机上就更明显了,这就是为什么Android 支持C++的原因。所以,在NB的电池或是能源出现之前, 如果你需要注重程序的运行性能和开发效率,并更关注程序的运性能,那么,应该首选 C++。这就是iOS开发也支持C++的原因。

今天的C++11中不但有更多更不错的东西,而且,还填了更多原来C++的坑。(参看: C++11 WikiC++ 11的主要特性

 

总结

  • C++并不完美,但学C++必然让你受益无穷。
  • 是那些不合格的、想对编程速成的程序员让C++变得坑多。

最后,非常感谢能和“ @简悦云风”,“ @淘宝诸霸”,“ @Laruence”一起讨论这个问题!无论你们的观点怎么样,我都和你们“在一起”,嘿嘿嘿……

(全文完)


(转载本站文章请注明作者和出处 酷壳 – CoolShell.cn ,请勿用于任何商业用途)

————————============ 感谢 42qu.com 为本站提供 VPS ============————————
您可能也喜欢:

C++和JAVA传统中积极的一面

C语言的演变史

JAVA的字符串拼接与性能

四个流行的Java连接池

20非常有用的Java程序片段
无觅

相关文章

相关 [C/C++语言 杂项资源 编程语言 C++ Java ] 推荐:

C/C++编程的现代习惯

- - idea's blog
相对于汇编语言是一门操作 CPU 寄存器的语言, C/C++ 是一门操作内存的语言, 这是传统的观点. 但现代的程序应用开发, 大多是把 C/C++ 当作一门应用层语言, 所以必须适当地减少对内存的关注. 这也是本文所要讲的 - C/C++ 编程的现代习惯.. 在一些古董级的编程书里, 你绝对看不到返回结构体或者类的实例, 它们告诉你"不能返回局部变量的内存".

如何学好C语言

- 夕角 - 酷壳 - CoolShell.cn
有人在酷壳的留言版上询问下面的问题. 我也遇到了和提问的老外一样的问题. 能给像遇到这样烦恼的程序员一点建议嘛. 我相信,这可能是很多朋友的问题,我以前也有这样的感觉,编程编到一定的时候,发现能力到了瓶颈,既不深,也不扎实,半吊子. 比如:你长期地使用Java和.NET ,这些有虚拟机的语言对于开发便利是便利,但是对于程序员来说可能并不太好,原因有两个:.

深入理解C语言

- clowwindy - 酷壳 - CoolShell.cn
Dennis Ritchie  过世了,他发明了C语言,一个影响深远并彻底改变世界的计算机语言. 一门经历40多年的到今天还长盛不衰的语言,今天很多语言都受到C的影响,C++,Java,C#,Perl, PHP, Javascript, 等等. 相信你看过本站的《C语言的谜题》还有《谁说C语言很简单.

C++的坑真的多吗?

- - 酷壳 - CoolShell.cn
先说明一下,我不希望本文变成语言争论贴. 希望下面的文章能让我们客观理性地了解C++这个语言. (另,我觉得技术争论不要停留在非黑即白的二元价值观上,这样争论无非就是比谁的嗓门大,比哪一方的观点强,毫无价值. 我们应该多看看技术是怎么演进的,怎么取舍的. 周五的时候,我在我的微博上发了一个贴说了一下一个网友给我发来的C++程序的规范和内存管理写的不是很好(后来我删除了,因为当事人要求),我并非批判,只是想说明其实程序员是需要一些“疫苗”的,并以此想开一个“程序员疫苗的网站”,结果,@简悦云风同学 直接回复到:“ 不要用 C++ 直接用 C , 就没那么多坑了.

64位平台C/C++开发注意事项

- 夕角 - 酷壳 - CoolShell.cn
在http://www.viva64.com/en/l/上例出了28个在64位平台上使用C/C++开发的注意事项,对于进入64位时代的程序员应该去看看这28个事项,这些英文读物对于有C/C++功底的朋友读起来应该并不难,我估计大约20-30分钟可以精读完一篇(或者更快),下面是这28个注意事项的列表.

C++11 中值得关注的几大变化(详解)

- michael - 酷壳 - CoolShell.cn
源文章来自前C++标准委员会的 Danny Kalev 的 The Biggest Changes in C++11 (and Why You Should Care),赖勇浩做了一个中文翻译在这里. 所以,我就不翻译了,我在这里仅对文中提到的这些变化“追问为什么要引入这些变化”的一个探讨,只有知道为了什么,用在什么地方,我们才能真正学到这个知识.

Why C++ ? 王者归来

- - 酷壳 - CoolShell.cn
因为又有人我去Quora的C2C站去回答问题去了,这回是 关于 @laiyonghao 的这篇有点争议的博文《2012 不宜进入的三个技术点》ActionScript,Thread 和 C++, C++争议的争议最大. (要我说,.NET比C++更需要慎重进入,呵). (如果你觉得那里的视频比较慢,你可以看 优酷上的视频)(英文听力好的同学可以看一样,因为都没有中文字幕).

C语言中史上最愚蠢的Bug

- E. Liu - 酷壳 - CoolShell.cn
本文来自“The most stupid C bug ever”,很有意思,分享给大家. 我相信这样的bug,就算你是高手你也会犯的. 首先,作者想用一段程序来创建一个文件,如果有文件名的话,就创建真正的文件,如果没有的话,就调用?tmpfile()?创建临时文件. 他这段程序就是HTTP下载的C程序.

在函数外存取局部变量的一个比喻

- rex - 酷壳 - CoolShell.cn
在StackOverflow上一这样一个关于C/C++的问题,问问题的人给了一个代码如下:. 你可以编译并运行这个代码(编译时会有一个Warning),结果是:5 8. 看上去你可以存取一个函数内的局部变量. 但这和我们理解的不一样——函数内的变量在函数退出时就被释放了,不应该在外部还可以被引用. 当然,对于C/C++熟悉的人都知道其实并不是真正的释放,你依然还可以通过内存地址去进行操作,这是C/C++的内存管理的不安全性——指针可以用来乱指.

为什么iComet比nginx-push-stream-module更好?

- - idea's blog
有些项目需要使用”Web服务器推(comet)技术时, 便因为 Web Server 用的是 Nginx 便选择了 nginx-push-stream-module, 但是根据我们的使用经验, nginx-push-stream-module 非常坑, 只适合同时在线几千人的小网站使用. 下面对比 iComet 服务器和 nginx-push-stream-module:.