被误解的C++——C++的缺陷和D的缺陷

longshanks 2007-10-13 04:53:19
C++的缺陷和D的缺陷
D语言,从字面上讲应当是在C/C++的基础上进了一位,其特性当然也进了一位。真是这样?也是,也不是。这得看你的出发点和价值观了。
D的定位在于继承C/C++的优势,但却更加易学易用。这种定位招人喜爱。C/C++的致命伤就在难学难用上。(不少人认为C++难学易用,我也持这种观点。但是既然要拿来同D比较,那我也只能跳一回票。只此一次,下不为例)。
我大致了解了一下D的特性,初步混了个脸熟。D差不多通过以下方式达到其目的的(如有错误或遗漏,请轻轻扔砖):
1. 新的语法,消除了一些缺陷,比如C++模板的>>问题(对此我持保留意见,一会儿再探讨)。同时,也优化了编译,提高了编译速度;
2. 将字符串、数组等内置,使其得到编译器的充分支持;
3. 用module代替头文件,抛弃了来自远古时代的遗物;
4. 用内置特性实现C++的一些库功能,比如traits;
5. 使用gc方便内存管理。但保留RAII功能;
6. 增加编译时计算支持。将C++部分通过模板实现的编译时计算放到内置特性中实现;
7. 增加mixin等mp支持。
限于我对D的了解,暂时只想到这些内容。总体上,可以认为,D的改进主要集中在将C++的很多由库实现转移到内置特性实现。内置特性的实现通常具有针对性,在语法上更简洁。此外,D也消减了一些不常用,但容易引起麻烦的特性。并且使一些特性自动化,以减少使用时的压力。
但在我看来,D所做的,是好心没办好事。因为D并没有真正抓住C++的核心问题,也没有很好地认清C++成功的关键。D继承了C++大部分的特性,这表明它认可C++的强大。(不然它留那么多特性干什么)。但是却没有能够从C++身上吸取真正的教训。
首先,C++的语法存在缺陷,Bjarne不止一次提到这个问题。根本原因可能是其语法是non-LR的。这个问题应该不难解决,我相信D已经解决了。但是,D大费周章地采用()和!()作为模板的操作符,似乎有欠妥当。首先,程序员的习惯,促使他们不容易接受这个新操作符;其次,()容易同函数参数产生混淆,不如<>来的直观;最后,过度放大<>存在的问题。<>的问题主要有两个,一个是同>>操作符冲突,这个问题在C++0x中已经解决。另一个是<3>4>问题,这种情况相对少见,而且可以利用<(3>4)>加以解决,并非关键性问题。为这些“鸡毛蒜皮”的问题而采用(),似乎不太值得。
其次,C++的最初动机也就是提供“更好的C”。此后,随着需求的不断扩展,逐步加入了各种高级特性。也就是说,Bjarne在最初的那段时间里,并不是那么高瞻远瞩。尽管他为C带来了OOP这种新的编程范型,但是从最后的结果来看,他的思维似乎也不够大胆。因此,C++的很多特性配合不好,有拼凑的感觉。D则站在了巨人的肩膀上,自然有更好的基础,更容易避免C++身上发生过的问题。
再来看D,目前所有的特性和能力,都没有超出原来C++的范畴,只是在其上做一些小修小补。从编程技术而言,相对C++没有显著的进步。不错,D在很大程度上简化了使用,但这是有代价的,它放弃了很多有用但难缠的特性。但这也会带来应用上的局限。
对于系统编程的定位,D似乎把易用性放在了过高的位置。实际上,后面我们将看到,D的面前同样存在另一条路可走。这条路,可以在C++的基础上提供更好、更强大的特性,但却可以消除很多C++的不足(但不是所有的,很多问题算不上缺陷,只是某种强大功能的副作用。强大的功能人人都想要,对吧?那就忍着点吧)。
再次,C++的很多问题来源于它逐步堆砌的特性。而且这些特性又是来源复杂,缺乏一致性。由此,很多first-class的概念被迫使用非first-class概念实现。
在这方面,D则做得更糟。比如,D强化了gp,但却没有引入concept,将来是否会引入,尚不得而知。这使得D无法根本上消除gp中遇到的诸多问题。相反D试图通过static if、static dispatch(C++常用的手段)之类的second-class特性完成concept这种first-class的需求。显然没有从C++中获得教训。
D象其他语言那样,把数组和字符串作为内置类型处理。在表面上,这是进步,用first-class的概念,用first-class的类型表示。但在我看来,这是一种倒退。说清这件事,就得看看什么是first-class的。传统上,类型分为内置类型和用户定义类型。而内置类型被界定为first-class的,得到编译器的优先照顾。然而,内置类型是否真的first-class呢?不,有比内置类型更first-class的,那就是类型。迷糊了?我慢慢说。
无论内置类型,还是用户定义类型,都是类型。我们传统上将他们区别对待。实际上,划分内置类型和用户定义类型的一个理想上的标准是原子性,即如果一个类型,(在语义上)不能分割了,便作为一个内置类型处理。为了保持移植性,象int之类的类型,被作为原子类型,而不考虑它内部字节的排列。同样string和array也是如此。但是,在很多系统中,比如SQL,为了处理方便,很多非原子性的类型也作为内置类型处理,比如datetime。这就表明,现实中内置类型和用户定义类型并没有严格的界限。
C++创建时有一个宗旨:让用户定义类型象内置类型一样处理。这句话是非常first-class的。也就是说,把所有类型一视同仁。如果能够做到,那么这门语言中,将只有类型这样一个first-class的概念,而不再分类。那这样得到的好处是什么呢?就是灵活性、扩展性、简洁性。
下一个问题,哪种类型是最first-class的?在C++中,内置类型是最first-class的,D也是一样。但事实上,最first-class并非内置类型,而是字节。汇编语言中,所有类型都可以看作字节或字节序列。也就是说,任何类型,无论内置类型还是用户定义类型,都是由字节组成。在此基础上,我们便可以用字节定义类型的静态结构,包括内置类型。也就是说,在静态结构定义上,内置类型完全可以同用户定义类型同等处理。
但是,内置类型和用户定义类型的行为是不同的,因为编译器并未把他们作为同一样东西处理。此时,我们便可以看到C++那句豪言壮语的作用了。当用户类型能够像内置类型一样的情况下,内置类型和用户定义类型将不做区分,完全可以一样处理。这便可以导致一个结果,就是所有类型,包括内置类型,都可以通过库而非编译器实现。于是,语言便可以扩展出丰富的“内置类型”。(呵呵,是不是有点metaprogramming的味道?DSL听了肯定高兴)。
C++朝这个方向努力做了,实现了绝大部分目标,但也未能100%地实现。比如,智能指针尚无法做到象内置指针一模一样的行为。(C++0x引入三个cast操作符重载后,情况就会好很多)。
这一点上D走出了一步,但却没能乘胜追击。D为所有类型,包括用户定义类型都定义了一组properties,用于获取类型的特性。在这一点上,所有的类型都一视同仁。但是,之后却依旧按传统将内置类型和用户定义类型隔离,并且将数组和字符串放回内置类型。再加上操作符重载上的一个明显退化,便坐失了统一类型处理的优势。
操作符重载,是使用户定义类型得以同内置类型一样处理的关键。C++除了少数的几个操作符外,其余都可以重载。这带来了极大的灵活性,(尽管如此,也未能使所有类型统一处理),但也带来了一些麻烦。(操作符重载带来的语义上的问题,不应该由语言负责,除非不想获得操作符重载的好处,否则必须承担此中的风险)。D缩小了重载的范围,避免一些混淆,以达到简化学习和使用的目的。(C#也是如此,但我并没有看到有多大的效果,我们重载操作符通常都集中在某些常用的操作符上,多数的操作符重载不会成为困扰我们的原因)。
D在操作符重载上的一个明显的变化,就是不使用操作符本身作为重载的定义,而是用等价的字符表示:
//C++
A operator+(A const&, int);
//D
class A
{
A opAdd(int v) {…}
int opPos(){…}
};
这种方式存在它的好处,但同时也带来了不足。好处是可以明确地区分统一操作符在不同情况下的语义,看起来非常明确。但是,这种方式使用起来却不如直接使用操作符本身来的直观,使用者必须识别这些操作符和对应的函数,这增加了记忆的负担。而operator+则侧重于记忆少数规则,理解规则,便可以举一反三,快速运用到其他操作符上。同时增加的这些内置函数记号,消耗了宝贵的关键字。两者的是非得失,全凭使用者的喜好,算不上什么天大的问题。只是我更喜欢记忆规则,而不是具体的记号。
D的操作符重载真正的问题在于取消了自由函数的重载形式。这样似乎简化了学习和使用,但实际上,只会把问题复杂化。成员型的操作符重载并非操作符最本质的形式。将操作符重载完全局限于成员函数,使得操作数无法交换。于是D通过允许定义opxxx_r()程序函数定义交换操作数的版本(复杂了吧)。对于另一个操作数类型,是否定义相应的操作符重载呢?这种方式迫使程序员不断地考虑这类问题。如果合作开发,那么必然增加了程序员间协调的负担。
无论如何,二元操作符的形式更接近自由函数。用自由函数加以表达,更加直观和明确,而且更便于集中处理。这篇文章,很好地阐述了过多的成员函数对封装性产生的影响。成员函数的一个好处是可以访问非public成员,但就像其他成员函数一样,成员型的操作符削弱了类型的封装性。如果不访问非public成员,那么作为成员,没有任何意义。
操作符重载是一个first-class的特性,但是D却使用了非first-class的形式表达。作为对C++操作符重载的简化,我认为合理的形式应当是:赋值操作符(=)、类型转换操作符,以及所有一元操作符,都采用成员的形式。二元以上的操作符都采用自由函数的形式。这样,规则并没有复杂多少,但却使得不同的操作符重载都能够以first-class的形式表示。
在first-class特性的问题上,另一个问题是D引以为豪的traits。Traits是非常有用的东西,使我们静态地获取类型的特征。traits在C++中起了非常重要的作用。所不同的是,C++通过类库的形式提供traits,而D则采用编译器内置特性提供。相比之下,受制于语言本身的特性,类库的实现难以提供完全的类型信息。而编译器内置特性,可以提供更完全的内容。
但是traits毕竟是非first-class的特性,C++0x通过引入first-class的concept,大大消除了对traits的依赖。具体的可以看这里这里这里。而traits背后真正的机制则是reflection。在这里reflection是真正的first-class机制,而traits是缺乏reflection(编译时)的一种替代或模拟。
D的问题在于,traits成为一种语言特性,当未来引入了concept和reflection之后,它将成为摆设,宝贵的语言特性资源被浪费。如果不引入concept和reflection,那么很多依赖于这些特性的问题无法得到解决,而traits也无法独立支撑这个局面。这会使D在这方面处于进退两难的境地。同样的问题也存在于mixin上。Mixin可以被看作一种Meta-Programming的机制,但并不完全,也非first-class特性。为真正解决现实中的问题,在GPL中引入Meta-Programming的需求越来越强烈。一旦引入真正的first-class的MP机制,那么mixin也会处于尴尬的地位。
这些问题表明,设计D的出发点存在问题。D仅仅试图在C++的基础上简化学习和使用,而不是采取更加本质,更加根本,更加first-class的手段来彻底解决C++面临的问题。就是说D并非一种面向未来设计的语言,仅仅关注眼前的蝇头小利。这种思维上的局限性,很容易使D在未来同更强大的语言,不仅仅是未来的C++,竞争的时候,处于不利的地位。因为限制已经造成,围栏已经建好,再想扩展便会受到很大的限制。
我还是那句话,如果D仅仅局限在修正C++的某些问题,那么说明它并没有从C++哪里吸取真正的教训。
最后,D似乎并没有充分意识到灵活性和程序员的选择对系统开发的重要性。C++的机巧性、危险性很大程度上是被过度放大了。在现实的开发过程中,我们绝大部分的时间,实际上都在老老实实地使用C++的普通功能,(但请记住,是在高级特性的支援下,使用普通功能)。这些动作都是常规的,成熟的。至于那些复杂、危险特性,则很少使用。即便使用,也是由专人(受过训练的,有免疫能力的)集中运用。在这样一个大环境下,语言的灵活性相比那些很cool的功能而言,更加重要。比如,使用多继承的权利。
多继承的主要问题在于钻石型继承带来的麻烦。但是,这种情况极少出现,通常也只在过度OO的设计中存在。在其他方面,多继承是非常容易处理和使用的。更重要的是,它是非常有用的,有时甚至是关键的(想想policy可以为我们消除多少类、继承和代码冗余,带来多大的灵活性)。为了一种很少出现的情况,把路整个地堵死,对于java这样的高层语言,或许可以忍受,但对于系统级的语言,是难以接受的。(至少对我如此)。更何况编译器可以准确地识别钻石型继承,并作出自动化处理(默认virtual继承),除非有特殊需要。
系统级语言不仅仅要求能够完成程序。鉴于系统开发的广泛性、灵活性,以及扩展性要求,语言的灵活性和程序员的选择是非常重要的。作为高附加值的开发任务,系统开发对于语言的复杂性的容忍能力是非常强的。
C++的问题是过于灵活,比如缺省情况下单参构造函数执行隐式类型转换,在应用中造成无数问题。一门新的语言完全可以通过合理地限制这种灵活性,消除问题。
我并不是说,C++那些缺陷和复杂是正当的。我只是想表明,通过消减语言特性,抑制语言的灵活性,限制使用者的选择,不是简化语言使用的正当手法。一种系统语言,应当以更加根本(本质)的方式解决C++的问题。在这一点上,D做的并不是很好。当D只专注于宣扬C++的缺陷,以及它所给出的解决方案时,便注定它无法成为超越C++的语言。在我看来,它应当把更多的精力花在如何提供更多first-class特性上,而不是用来标榜自己的那一点点进步。
...全文
1170 38 打赏 收藏 转发到动态 举报
写回复
用AI写文章
38 条回复
切换为时间正序
请发表友善的回复…
发表回复
mycsxy 2007-10-30
  • 打赏
  • 举报
回复
又回来学C
califord 2007-10-17
  • 打赏
  • 举报
回复
又回来学C++
jspxnet 2007-10-17
  • 打赏
  • 举报
回复
我极度讨厌,c++中是人是鬼都来定义一个类型,结果不管是库,还是软件,到处都是莫明其妙的类型。没有一点规范的思想。
一个简单的String类型,结果都有n种。
xenix 2007-10-16
  • 打赏
  • 举报
回复
* NaiNaiGeXiong
* 黑猫猫
* 等 级:


发表于:2007-10-14 22:48:3715楼 得分:0
D 语言的问题就是现在就一个人在开发这个语言,他是编译器高手是肯定的,但他未必就是语言设计高手啊,再有就是D语言的不稳定,从看初的1.0版到现在的 2.005版,就才半看时间哦,变更这么快,真是不知道怎么说那个老头才好了,本以为是很好的语言,但他的这种开发方法真是不敢恭维了哦,期待到D语言 3.0版出来时会稳定下来,如果就是一个人的话,我想是永远做不出C++语言的继承者来的,希望D语言不要再让喜欢她的人伤心……
>>>>>>>>>>>>>>>>>>>>>.
真是爱之深责之切啊,呵呵。好消息是D早已经有了 1.0 稳定版和2.0实验版两个分支,还有,至少现在是两个高人设计语言了 :)
sjjf 2007-10-16
  • 打赏
  • 举报
回复
执着已入相
gogowhy 2007-10-16
  • 打赏
  • 举报
回复
m
caitian6 2007-10-16
  • 打赏
  • 举报
回复
支持楼主辛勤劳动,加油!!!!!!
等着看更精彩的文章。
huzhangyou 2007-10-16
  • 打赏
  • 举报
回复
略微玩了一下
印象中那个 大象的 环境不错
或许是年纪大了 怎么也对新事物提不起兴趣
还是C++觉得顺手
不管如何
开发来说 能达到目标就是最好方法 过程或许对开发者来说重要
对客户来说 无所谓
sclzmbie 2007-10-16
  • 打赏
  • 举报
回复
呵呵,我也觉得C++大牛未必能够胜任创造一个新的,广为使用的系统级语言。
an_bachelor 2007-10-16
  • 打赏
  • 举报
回复
我们早用到E语言了 你们才用到d 太落后了
longshanks 2007-10-15
  • 打赏
  • 举报
回复
底层系统有c 汇编.

上面有java .net平台

C++到底 那里适合呢?

看过云风那篇回到c 觉得c++ 真的 很难找到它的定位了.
=========================
你已经有答案了嘛。:)
呵呵,在中间呀。:-P
Mephisto_76 2007-10-15
  • 打赏
  • 举报
回复
顶一下再说了。
hertcloud 2007-10-15
  • 打赏
  • 举报
回复
底层系统有c 汇编.

上面有java .net平台

C++到底 那里适合呢?

看过云风那篇回到c 觉得c++ 真的 很难找到它的定位了.
gogovista 2007-10-15
  • 打赏
  • 举报
回复
没有concept
是D的一大缺陷
zdh108114113 2007-10-15
  • 打赏
  • 举报
回复
进化 发展 乱!!!
ouyh12345 2007-10-15
  • 打赏
  • 举报
回复
学习
BluntBlade 2007-10-15
  • 打赏
  • 举报
回复
写程序应该首先让人感觉自然,然后才是优美。如果不自然,必定会埋入Bug的种子。所以我觉得,只要语言能解决问题,那就用它。如果不适用,再慢慢改进甚至是改变它。

至少,D是一种应用上的进步。工具不是以用为本么?
longshanks 2007-10-15
  • 打赏
  • 举报
回复
D语言的问题就是现在就一个人在开发这个语言,他是编译器高手是肯定的,但他未必就是语言设计高手啊,再有就是D语言的不稳定,从看初的1.0版到现在的 2.005版,就才半看时间哦,变更这么快,真是不知道怎么说那个老头才好了,本以为是很好的语言,但他的这种开发方法真是不敢恭维了哦,期待到D语言 3.0版出来时会稳定下来,如果就是一个人的话,我想是永远做不出C++语言的继承者来的,希望D语言不要再让喜欢她的人伤心……
=======================================
这话说在点子上了。
D目前的问题是初始设计的约束太强,以至于一开始就定型了。随着关注和使用的人越来越多,增加这样或那样特性的压力会越来越大。此时,只有开放的设计才能在扩展的同时,减少语言的不相容。
这种情况在C++身上发生过。只是原始的设计(特别是C)有足够的开放性和灵活性,才使得C++在跨越了三代编程范式之后,依然没有崩溃。即便如此,这种“添油战术”仍然使得C++充满了缺陷,令人遗憾。
这就是我为什么说D还没有从C++身上吸取教训的原因。
nickshen_qidian 2007-10-15
  • 打赏
  • 举报
回复
不了解D,支持一下。
加载更多回复(18)

64,663

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧