2011年8月7日星期日

从C++的一个特性到设计原则再到哲学

    最近在看C++的设计和演化,里面讲到算符重载。关于这个,Effactive C++里面明确说明,不要试图重载&&和||算符。因为这个重载造成的结果和默认不符(Not same with the default)。
    &&和||有什么特殊?熟悉C的朋友考虑这么一个问题。if(i && ++i)的作用是什么?基本来说,这个语句是判断i是否为0或者-1的,并且有个额外效果就是对i进行自增。但是,如果i == 0,则不进行自增,这就是&&的短路求值原则。这个原则产生了一系列写法,例如sh中常见的 [ -z "$ABC" ] && { ... }。
    不过当重载了&&或者||后,就破坏了短路求值原则。因为C系列语言是应用序语言,参数先求值。所以后参数*一定*会被求值,无论前参数的值是多少。
    更加悲崔的是,这个破坏了最小惊讶原则,或者叫做知识内隐原则。当你使用一个知识的时候,你会根据自己的经验对这个知识做内隐的预期。例如,虽然螺丝有左螺纹也有右螺纹,然而你在拧螺丝的时候,多数预期是顺时针拧紧。不论其理由,这个已经成为常态。同样,有下压把手的门是扇页门,画着杯子的店家是咖啡店和茶馆,画着裙子的厕所是女厕,这些都是你对知识内隐的预期。破坏这个预期,相当于把螺丝改为反向,下压把手的门改成移门,画着杯子的店家是古董店,男厕画裙子一样,会让人感到不知所措。大家会莫名其妙的绕出去,确认门上画的确实是裙子,走进去再看到男厕,感到世界莫名其妙。
    同样的道理,如果一个对象使用了&&重载,程序员唯一能够快速发现的机会就是在调试时单步了&&的语句。如果他运气不好,可能在数个小时内都找不到理由,直到反汇编目标代码为止。
    那C++为什么设计算符重载?那是设计给需要的算符用的。其实C++一直是一个矛盾的设计,一方面他认为,程序员是不可信的,所以C++里面有隔离保护系统,例如私有成员函数和变量。另一方面,他又认为程序员应当对自己的行为负责,因此他设计了复杂的算符重载,复杂的继承系统,并期待程序员能够按照正确的方法使用。这是一个奇妙的,矛盾的设计思路,反映设计者自身的冲突(例如多人设计),或者C++设计者的实用主义倾向(选择最实用的设计)。python语言的思路相对统一,他认为程序员应当为自己的行为负责,所以python的隔离系统都是伪系统。而java的思路也相对统一,他认为程序员是不可信的,所以java才会搞出复杂的架构哲学。

没有评论: