- A+
阅读前提:
注意:这边文章是供软件开发专家欣赏,而并非供GreaterThanZero的用户欣赏。
阅读者需要熟悉C++并且至少掌握一门其它面向对象编程语言。
概要:
在我的印象里"复杂性文化"深深植根于C++社区里。在这片文章里面,我将带领大家探索C++历史,以期从中理解这种现象。
我是一个C++的长期用户,并且作为一名作者和鼓吹者在C++社区里面活跃了若干年。是的,GreaterThanZero的后端大量使用了Java而非C++。这是否意味着我加入了浩浩荡荡的反C++大军?不,不是这样,我能给个证明:搜索“C++ auto and decltype",你就会发现第一个结果就是我几周前写的一片文章。用Java实现GreaterThanZero的数学后端,是基于一些其它方面的考虑,主要是因为需要使用Google App Engine作为发布平台。
每当我给GreaterThanZero写JavaScript或者Java代码的时候,我经常发现我自己在思考这个问题,“哈,so easy.妈妈再也不用担心我的代码了。”接着,我不得不惭愧的承认,我很失望。因为没我没有机会证明自己比别人更聪明。我不得不去安慰自己说,我实现的数学不简单呢。
想要给人们留下你的代码极其复杂的印象而不是你的软件,是一种不够成熟且工程做的不好表现。我就干过这种事。无需多言,这是我需要克服的。我不会将我自己的缺点和人格弱点怪罪他人。但是,我觉得如果说想要给别人留下代码是复杂的印象的这种坏习惯只能在一种“复杂性文化中”才能诞生并且流行起来,在这种文化中,复杂以及难以理解的代码虽然未必得到承认和鼓励,但是至少是可以忍受的。虽然我不知道这有多流行,但从我自己的经验来看,这种复杂性文化是C++社区让人备受折磨的一部分。
我相信你若要解决一个难题,就必须首先了解其根源。所以,我将通过追溯C++和面向对象编程的根源,来尝试解释在一些C++社区中会碰到的对复杂文化的偏好现象。
广为传播的共识中,第一种面向对象语言是Simula 67。早期的为OO模式的出现做出贡献的开发工作发生在MIT的List领域的工作,特别是在人工智能组。我不清楚Simula 67的发明者多大程度上受到了MIT的工作的影响。然而,有意或无意的,Simula 67采用了引用语法(reference semantics)以及gb(garbage collection)特性(后面详细叙说),这的确显示对Lisp的一些延续。
那C++又是如何参与进来的呢?正如这篇文章: Wikipedia article on Simula中所说:“C++的创始人, Bjarne Stroustrup认为在开发C++过程中,为了把Simula具有的提高开发效能的特性和底层语言如BCPL所具有的原生的计算速度特性相结合,Simula 67对他的影响最大。
公平的说,可以认为Simula 67和OO模式的出现是一种创新的,如果不算革命性的,观念从已有的有效实践中诞生的例子。我所说的Lisp使用了引用语法( reference semantics )和gb特性的事实就是证据。我认为,C++把OO模式和高效性以及C具有的底层控制能力相结合的方法是另一种形式的努力。为得到最好的效果,把其他两个完全独立的特性包含进来,在我眼里这是一种实验。我这样说不是表达批评之意,相反,我相信人类的好奇心和无休止的对更优方案的追求将使得这种实验得到有效执行。
C++的实验并没有失败,那么多的人用C++写出了那么多的令人惊讶的有用软件足以打破任何人所宣称的C++失败的言论。另一方面,就我而言,C++却也并非完全成功了,要知道,有人会说,这似乎很有魅力,似乎OO特性和C的底层控制都是为对方而准备的。在我看来,有很多很明显的不一致和不协调,它们有两种展示自己的方法:
1. C++程序员不得不花费大量工作到no other end,而不是使语言工作。
2. C++的进步是得到了一个处理语言内核的不一致和不协调性的影响的外部驱动。
要得到上面说法的证据,考虑除了C++和Objective C的OO编程语言,想想Java和C#,下面的场景:
x = y;
这样会发生什么?它使得变脸x引用了y引用的对象,并且让垃圾回收器处理上面的表达式引起的对x之前引用对象的影响。这就叫 reference semantics,这也发生在函数参数传递时。
现在看看C++中相同的行,此时假设x和y都是用户自定义类型的对象。这样的行都做了些什么呢?是的,在我们没有看到用户定义类型的源代码的时候,我们当然不知道做了什么,而且复制赋值操作符还可能出现重载。然而有默认行为,一般来说,默认的行为就是x指向的对象和y指向的对象具有同样的状态。技术上来说,状态相同是通过这种方式获得的:对象的复制赋值操作符可以递归地应用到对象的成员,直到对象的成员是基本类型比如int为止,这时,将会进行正常的旧的值赋值。这也称作值语义,然而我更愿意称其为状态语义。对象是没有值的,它们只有状态,所以在这里发生了这样的转换行为。
大多数人都同意变量之间赋值是编程语言设计的一部分。在C++里,用户自定义类型的变量之间赋值涉及到对象拷贝。不过经验表明复制对象很困难。下面从comp.lang.list的常见问题解答(FAQ)页面的摘录对此进行了很好的总结:“问:为什么这种编程语言没有深度拷贝功能? 答:拷贝任意结构或者对象需要根据上下文来确定正确的拷贝应该怎么做(哦,原来是这样!)。”在C++里,对对象的拷贝就会遇到例如把C风格的指针当作类成员看待这样的技术问题,这样的事实使得对象之间的拷贝更加难以实现。我相信,有证据支持我上面的 猜想1:C++程序员不得不更加努力地确保变量之间的赋值功能将像基本类型那样正确地运行。这里仅仅只有一个例子;我将准备给出更多这样的例子。
为了寻找支持上面我的猜想2的证据,我请你看看rvalue引用。Rvalue引用给出了对象移动语义,而且rvalue引用解决了完全转发问题。对用户自定义的类型使用引用语义和垃圾回收的古典面向对象的语言既没有移动语义所关注的性能问题,也没有完全转发问题。C++自己正在解决这儿提到的问题,而且它已经按照对应用编程人员不透明的方式这么做了:rvalue引用使得应用编程人员可以看到更多的语言界面。再者,这只是对我猜想的提供证据的一个例子;我将提供更多这样的例子。
据说一个公司或者组织都有自己的DNA。如果编程语言也有自己的DNA是真的话,那么对我来说,C++也应该内置了自己的DNA。
- 希望关注编程语言内部的问题,而且语言内部的设计会大大地影响到编程人员日常生活里。
- 更加关注实验、更加关注功能所取得的即刻好处,而问题则留给以后在修改。
- 可以稍稍增加编程语言的体积以及提供给用户的界面这种倾向,而很少关心功能之间交互所产生的混合复杂性。
另外,不要为出于虚荣心而编写的复杂的并且实验性的代码这样不可原谅的行为请求原谅,我真正感觉到诱使我这么做的是一种文化,这种文化好的一面是根本不关心如何如何的复杂,坏的一面是鼓励增加复杂性。我相信:哪些要求用户懂得许多并且让用户有更多的的机会狂热地进行试验性编程和复杂性编程的语言管理者应该有责任在创建让语言简化的文化中付出更多努力。说实话,我根本看不到这样的努力。站在像Go语言这样的竞争者的角度来看的话,我对C++的未来很悲观!
原文地址:http://blog.greaterthanzero.com/post/58482859780/c-and-the-culture-of-complexity