免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: lenky0401
打印 上一主题 下一主题

C/C++最近很混乱 [复制链接]

论坛徽章:
0
21 [报告]
发表于 2011-11-18 12:25 |只看该作者
本帖最后由 狗蛋 于 2011-11-18 12:34 编辑

回复 13# bruceteen


    这就是所谓淫者见淫。


比如这个:

Linux之父炮轰C++:糟糕程序员的垃圾语言
http://www.google.com.hk/search? ... ;btnG=Google+Search

不知你们看了,反应如何。



这个争论发生时,正是我对C++的痴迷期:
不仅早在03年或04初,我就买了侯捷的STL源码剖析,甚至去啃当时还非常激进、号称只有未来的编译器才能支持的loki库(因为这个库使用了很多“疯狂”的模板特性,这种特性在当时没有任何编译器可以支持——换句话说,这是一个先进到无法编译的模板库)。

而且,我还参考业界有名的几个经典实现,针对实际需要改进/特化了STL库的内存池,而且这个池一直跑在一个工控系统上(做过仪表的应该知道这种系统对稳定性有怎样的要求);同时,部分修改/特化了STL库的hash_map,也用在那个系统中。

甚至,07年我还用模板+继承+宏,完成了一个POD结构体类型自动打包成XML的库。这个库就俩接口: convert2XML(const T&)和conver2POD(const char * , T&),用起来和c函数调用一模一样,但却可以根据传入的POD类型自动选择合适的转换策略。

至于面向对象,早在97年已经对继承多态什么的弄的滚瓜烂熟,恨只恨当时学校机房竟然没有C++编译器,不过这不耽误我在头脑里思考自己做过的程序如果改用面向对象的话,该怎么设计。
以致于后来(05年),华为有个老想着给我穿小鞋的“高手”领导,说某个开源的unittest系统的面向对象设计非常高明,他们当时十几个人都是各部门抽出来的高手,研究了很久都没看懂;吹完了忽然突发奇想交给我个任务:写个文档,下次开会给大家讲讲,让大家都懂。
我花了一个晚上,写了个深入浅出的ppt,做到了。而且还总结了这个系统的设计思路,指出了这个系统不尽人意之处。

甚至,当时一个牛人,好像是周伟明还是谁,曾在自己博客上举例说用模板会导致代码膨胀,比如stack(T)会出现多份代码云云;我回帖反驳说你用stack(size_t)也一样,大小是必须指定的,区别只是代码写在哪里而已,和代码膨胀有什么关系?弄得他很是下不来台。



就是在这个背景下,这桩子事出现了: Linux之父炮轰C++:糟糕程序员的垃圾语言
这是当年的原帖:
http://blog.csdn.net/turingbook/article/details/1775488





我敢肯定一点,看到这个,换了本版这几天闹腾的那些人,肯定是立刻跳出来参与c教徒和c++教徒的“圣战”;把这样一个本来就带着情绪带着火药味夹杂着bullshit的学术争论,彻彻底底拖到泼妇骂街的低级水平上。

——那啥,c教的红衣大主教都打上门来了,c++的狂信者们,你们在哪里?你们对神的虔诚,哪里去了?



可惜,在我们,乃至世界上无数程序员眼里,这并不是——如同本版闹事者所以为的那样——宗教战争。



我们在实践中,早已发现c++的弊端。自己独立设计、编写的代码越多,这弊端也就越发明显。

比如,拿最容易说清楚的内存池来说吧。

如果用C写,很简单,void * pool_malloc(pool*, size_t)和void pool_free(pool*, void *)即可:用户拿去做什么,我不管;我在里面如何验证安全、加越界检查、内存泄漏检查,用户也不知道。

更进一步,如果想存特定大小的海量小对象,该怎么办?
增加个init_pool(pool*, size_t)接口即可。我在里面可以什么都不做,也可以切换到针对被管理单元大小一致优化过的方案上。

这就是所谓的 内存管理对业务透明,或者说 内存管理逻辑和其它业务逻辑正交。


这种模块非常独立,好写好测好掌握变化。





但,换了C++,怎么办?

首先,malloc来的内存,c++不能直接用。因为你不能调用构造函数(没错,C++阻止不了你强制调用构造函数,但这并不能正确完成初始化)。所以,你需要placement new。

placement new很麻烦。不仅是申请时需要指定从哪里分配,释放时也要指定由谁回收。而这,显然是麻烦的根源。


另外,因为内存池越是专用,就越容易设计的好;做通用了,性能反而不会比直接用原生内存管理来的高。
所以,为准备放到池中的类设计自己的operator new,就是更好的选择了。

但是,你必须知道,这个operator new并不能被子类直接拿来就用,因为子类往往总会比基类大一点点。加上内存池的专用性,或许你要用非虚析构函数来阻止由它继承——而非虚析构函数=禁止继承这点,很多程序员并不知道。


或许,直接使用STL的trait机制是更好的选择: 针对特定对象,指定给它特定的内存池好了。

不过,我想这个trait声明拿出来,那些出来打圣战的C++狂信者,多半是看不懂的。


另一个方面,如果规定所有被管理类都提供init方法,这个方法可以把自己重置为初始状态,那么这样做的内存池也非常棒:可以针对固定大小优化;不考虑所存对象细节;接口简单、朴素。
当然,你可以争辩说,这是把复杂度丢给了使用者。



你看,就这么个内存池,你得和多少其它特性打交道;为了合理设计它,你得多考虑多少东西。


这就是“非正交语言特性”带来的心智负担:用C写的池早已轻松支持写越界检查、内存泄漏报告了;C++还在纠结内存池是否需要知道所存对象的类型、用不用placement new、如何阻止重载了operator new的类被继承等等方面。

甚至,我还见过一个dephi写的游戏支持库,用的是最基础的pascal,水平非常高。他的内存管理实现了垃圾自动回收、对象池等等功能;核心代码也就几百行。
用这个库,你可以轻易为指定某个游戏物品执行一个动画:只要你产生个动画对象赋给它就完事;动画执行完了或者被其它事件中断了,这个对象会自动被回收。

这种东西,用C++来做——做到库的标准,也就是必须和其它特性互不冲突,或者有冲突可自动探测并触发编译器警报——就没那么容易了。
难度不仅仅在于代码量容易膨胀,还包括代码是否正确:因为这个池并不是一个单纯的池,它必须考虑其它对象怎么用它!





好在,我是个很实际的理想主义者。
当初写那么多模板、面向对象的东西,我知道自己没精力没时间去理顺它们和其它诘屈聱牙的东西之间的关系。
越是知道得多,这种无力感越是强烈——要能像印度程序员那样,一个星期只写200行代码,我可以做到滴水不漏。
但一天几百甚至数千行原创代码……我还真不敢保证。

对策是: 我用C++/STL写代码,但不提供C++/STL的接口!

我会清晰定义模块边界,然后用一组简单的C函数对外提供服务。这样,我才可以控制,保证使用者不会捣毁一切。
如果必须提供C++类/STL接口,那么我会提供最简单最朴素的,一点花巧没有。这样,我才可以保证,使用者随便怎么玩,都不会超脱我的控制范围。

即便如此,STL实例化时,不同编译单元里会出现同一静态对象的多个副本,仍然在控制范围之外。

更可怕的,模块外部是稳定了,其内部,仍然要考虑维护问题。
而我,并不能保证后来的维护者,不会用诘屈聱牙的东西来挑战我的逻辑。
我用大段的注释、说明指出可能的隐患和规避办法。因为我实在没有时间/精力用C++本身的机制一一堵住所有这些隐患。


——可是,为什么我要在本身已经足够良好的模块设计之外,不屈不挠的花费诺大精力为C++本身的非正交特性们擦屁股呢?



这让我很怀疑,C++是不是走错路子了。或者,至少是,在中国广为宣传的种种C++技巧,走错路子了。

Torvalds的劈头盖脸甩给cpper的bullshit,一度让我非常恼怒。但冷静下来后,我知道,恼怒只是因为被他说中了。




事实上,如果定义好模块边界后,可以用C提供接口的话;模块内部的子模块,子模块内部更小的功能模块,一样可以用C来提供接口。
而这种简单可靠的接口,可以让程序编写者忽略很多东西——诸如new和c原生数组的非正交、new和内存分配方式的非正交、模板和类的冲突、类和拷贝构造函数/自动类型转换、以及运算符重载前后的运算规则等等等等:我要的是功能,要的是便于使用的稳定接口,这些都干嘛啊过来瞎掺和?

于是,我逐渐在自己的模块内部,根据清晰的模块划分,以C或最简单的C++类方式提供接口;只有确认引入特性不会产生棘手的side effect时,才会利用那些“高级”特性。这让我少写了很多东西,少考虑了很多额外的东西。
久而久之,我拒绝用C++/面向对象的方式思考问题,拒绝把华丽的特性纳入考量范围。尤其是模块级别。


我很庆幸。当我见到tovalds丢来的一坨bullshit时,没有见到那么多的圣战众。
否则,也许还要走更多的弯路,才会回头。

论坛徽章:
0
22 [报告]
发表于 2011-11-19 01:13 |只看该作者
>>"或许你要用非虚析构函数来阻止由它继承——而非虚析构函数=禁止继承这点"
这一点似乎应该是把析构写成非公开才能成为不能被继承的类

>>tovalds的bullshit
是事情是有道理,但是又如BS说的,好处谁用谁知道。所以个人不发表意见。

>>用法
根据我看你写的内容,我和你的用法类似,cpp的确给思维混乱,设计不明的事情带来了很多负荷。
个人觉得RAII比较有用,至于模板和虚函数,看情况。另外new/delete和malloc/free之间的矛盾,的确是一个让人非常不舒服的地方,特别是有realloc却没有renew,就地构造还要就地析构。所以我个人是要么用malloc/free,要么用new/delete,不要混用。而且目前性能测评下来,new和malloc差别不大。

论坛徽章:
0
23 [报告]
发表于 2011-11-19 03:27 |只看该作者
>> 首先,malloc来的内存,c++不能直接用。因为你不能调用构造函数(没错,C++阻止不了你强制调用构造函数,但这并不能正确完成初始化)。所以,你需要placement new。placement new很麻烦。不仅是申请时需要指定从哪里分配,释放时也要指定由谁回收。而这,显然是麻烦的根源。

麻烦的根源不在于placement new,而在于你怎么来看。不使用placement new也可以做出高效的内存池。只不过可能在某些语境下不如预期那么高效。那还是有两种选择:加上placement new、或者不使用那个语境。这样的选择在各种工程中随处可见:我有变态的需求,你觉得成本太高,那好,我们妥协一下。即便是C也不是万能的,每种内存池也只能应付某一些场合,而无法应付其他场合。

>> 但是,你必须知道,这个operator new并不能被子类直接拿来就用,因为子类往往总会比基类大一点点。加上内存池的专用性,或许你要用非虚析构函数来阻止由它继承——而非虚析构函数=禁止继承这点,很多程序员并不知道。

你非要把固定对象大小的object pool用在通用对象上,然后试图削足适履……这种做法本身就有问题。你要用object pool,你就在注释或者文档中写明,只对固定大小类型负责高效处理,子类的new请求抛给其他内存池,由此带来的效率下降由使用者负责。如果你非要使用语言特性来禁止,让编译器报错,那是你太高看C++了。但是,你也是如此高看C的吗?绝对不是吧。像object A的pool里非要分配个object B出来,这是人类都无法阻止的。

>>这就是“非正交语言特性”带来的心智负担:用C写的池早已轻松支持写越界检查、内存泄漏报告了;C++还在纠结内存池是否需要知道所存对象的类型、用不用placement new、如何阻止重载了operator new的类被继承等等方面。

我写过C++的内存池,也可以越界检查、内存泄漏报告,到现在都在使用,毫无压力。我也承认实现的过程中考虑过很多,但是一旦完成,我就再也没去管它了,只用在每次debug后轻松的看结果就行。这种所谓的“心智负担”只是在工程实现中才会有。但是即便是用C,有哪个工程设计实现过程没有“心智负担”的呢?每个工程都得按照需求考虑这,考虑那,这是很正常的事情。如果说那些语言特性是“心智负担”,那是多虑了。你不用那个特性,就自然没有负担了。C++提供很多很多的特性,你要把他们全都看作负担,那就真的是负担。你要把它就当C用,那也没什么负担,对吧。C++11里面又增加了很多新的“负担”,估计已经超出大多数人的承受范围了,悲观的来看,C++离灭亡不远了。真的会是这样吗?

>> Torvalds的劈头盖脸甩给cpper的bullshit,一度让我非常恼怒。但冷静下来后,我知道,恼怒只是因为被他说中了。

他说bullshit,就让他说去。我记得是谁说的“C++是反紧凑的”,就是说的C++的这些个语言特性之间的相容性。本人觉得语言特性相容性低甚至排斥也没什么不好,我总能在这些特性中挑出一些组成紧凑的子集,而且我可以组合出多个子集以轻松应付不同的情况。所以从坏的方面想,这些都是负担。从好的方面想,这些都是选择。所以你说C++有比C更多的负担,而我说C++有比C有更多的选择。

论坛徽章:
0
24 [报告]
发表于 2011-11-19 09:00 |只看该作者
C、C++不混乱,只不过是论坛混乱

论坛徽章:
0
25 [报告]
发表于 2011-11-19 12:49 |只看该作者
怎么?印度程序员一个星期只写200行代码?

论坛徽章:
0
26 [报告]
发表于 2011-11-19 15:54 |只看该作者
怎么?印度程序员一个星期只写200行代码?
光明-使者 发表于 2011-11-19 12:49



    其余时间忙着生孩子

论坛徽章:
0
27 [报告]
发表于 2011-11-19 16:05 |只看该作者
回复 21# 狗蛋

你督错洞了。
C++或许有价值使人痴迷,但是我是懒得浪费时间。有这么空宁可去做点数学题活跃一下脑细胞。

Linus从他公开言论和做过的事情来看就一C++外行。至于他提到的东西,有多少学术?归结起来又可以引用某人的话了,“人的问题”。为什么现在还会有人评论语言的时候还拿这种不着边际的论据?
确实,C++的弊端很明显;因此,发现不算很难。但:1.弄个清单成不?2.为什么会有这些弊端呢?

>>这样,我才可以控制,保证使用者不会捣毁一切。
你应该审视一下你写的东西的目的是什么。C/C++为什么需要/可以“相信程序员”?

>>更可怕的,模块外部是稳定了,其内部,仍然要考虑维护问题。
跟具体语言有关么。你是要说,C++写的东西的内部维护起来很容易有问题?那么,怪谁?是你知道得太多了,还是太少了?

>>而我,并不能保证后来的维护者,不会用诘屈聱牙的东西来挑战我的逻辑。
“人的问题”不提了,下同。

——可是,为什么我要在本身已经足够良好的模块设计之外,不屈不挠的花费诺大精力为C++本身的非正交特性们擦屁股呢?
>>C++比C多出来的特性是给谁擦屁股?



或者,至少是,在中国广为宣传的种种C++技巧,走错路子了。
>>宣传纯OO是犯傻。宣传复杂没有错,免得有些人太天真。

Torvalds的劈头盖脸甩给cpper的bullshit,一度让我非常恼怒。但冷静下来后,我知道,恼怒只是因为被他说中了。
>>我刚看到只是恼怒太吊胃口了。不过后来发现好像真没什么更多的技术细节,于是释然。

久而久之,我拒绝用C++/面向对象的方式思考问题,拒绝把华丽的特性纳入考量范围。尤其是模块级别。
>>把语言和面向对象并列,叫人肿么吐槽……你的思维居然还跟着一种人工语言走?

论坛徽章:
0
28 [报告]
发表于 2011-11-19 16:56 |只看该作者
写了很多,被自动刷新吃了

懒得再写了。

一句话,能把C++用好的,或者是知道自己菜,因而不去碰那些“高级”特性或者碰了就把那块隔离开的;或者是牛的了解了一切高级特性的;最危险的,就是那些自命不凡,以为就自己懂了点“骇人的高级特性”,不拿出来炫炫就不舒服的。



从特性萃取、traits到模板特化、偏特化;然后再加上运算符重载;在配合上各种范式——没错,你简直可以搞一个量子叠加态的东西,这个东西会根据你的需要,产生最适合你的代码!

的的确确,C++的确提供了很多好东西;这些好东西在高手手里的确可能衍生出非常好的东西

——这点没有任何人怀疑,不需要圣战徒们反反复复出来强调,更不需要用你知道的、说出来就光荣无比、就足以鄙视Torvalds是个不懂C++的SB的高级概念一次次证明你的高人一等。


现在的问题就在于,你怎么把这“好东西”衍生出来?

我说你越用高级概念,就越是衍生不出来,承认不承认?

我说就连写STL的,吭哧了十几年也不过就是吭哧出了STL那么几个简单容器/算法,对不对?


最后,当你老板——或者你自己——需要搞个东西出来卖钱的时候,第一件事就是控制复杂度,对不对?


这就是我的意思。

不像你们圣战徒想象的那样,我和22楼以及周伟明、云风等人的意见一致:我们并不完全排斥C++,排斥了解那些高级概念——正如某些圣战徒嘲笑的那样,一边反对一边自己模拟实现那些高级概念——相反,我们了解它们,而且了解的早得多,用的也早得多,偷窥黑盒子内部也早得多。

但区别是,我们拿它当知识储备;在跟进、控制团队出产品时,我们更倾向于控制风险,减少不确定因素,而不是不顾风险,只管自己炫耀。


我们,要的是产品,不是paper;我们的产品要上市卖钱,不是项目拖时间越长上面拨款越多。

话不投机,不浪费时间,回去玩WOW了。

论坛徽章:
0
29 [报告]
发表于 2011-11-20 11:51 |只看该作者
几个帖子被关闭了,各位先冷静一下,不要再做无谓的争吵。本无所谓谁对谁不对,都相互容忍一点。
谢谢。{:3_201:}

论坛徽章:
0
30 [报告]
发表于 2011-11-20 11:53 |只看该作者
另外,各位不要再新开专门的争论帖了,还有异议的或还有话说的请在本主题内跟帖。{:3_186:}
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP