D语言概览
相信很多人都没有听说过D语言,下面我来介绍一下D语言特点。
D 语言能做什么?
D语言是一种跨平台的编程语言,它的大部分应用程序都可以在linux和windows下执行,并且不需要任何虚拟机。它可以支持32位和64位的开发。D语言可以做下面的所有东西。
- GUI界面的软件开发
- WEB平台的开发
- 游戏开发
- 与其它脚本语言的开发,PYTHON,LISP,TCL,LUA
GUI界面的软件开发
D语言有很多开源的GUI类库,并且都有跨平台性,比较著名有下面几个:
- D graphical User Interface (DUI)
- DFL
- DWT
- D Framework
- DIUP
- Harmonia
- MinWin
- SDWF
- Terra
- wxD
详情可参见 http://www.prowiki.org/wiki4d/wiki.cgi?AvailableGuiLibraries
WEB平台的开发
- Mango
- DSP
- D Server Platform
游戏开发
- Empire
- Torus Trooper
- Auf sie mit Gebrüll!
- AREA2048
与其它脚本语言的开发,PYTHON,LISP,TCL,LUA
- PYD
- LISP for D
- TCL for D
- LUA for D
什么是 D 语言?
D 是一种通用的系统和应用编程语言。它是比 C++ 更高级的语言,同时还保持了生成高效代码以及直接访问操作系统API和硬件的能力。D 很适合于编写从中等规模到那些由团队合作完成、数百万行代码规模的各种程序。D 易于学习,为编程者提供了很多便利,并且适用各种野心勃勃的编译器优化技术。
D 不是脚本语言,也不是一种解释型语言。它不需要虚拟机、宗教、或者高于一切的哲学。它是给实际的编程者使用的实际的语言,它帮助编程者快速、可靠的完成易于维护、易于理解的代码。
D 是数十年来实现多种语言编译器的经验的积累,是用那些语言构造大型工程的尝试的积累。D 从那些语言(主要是 C++ )那里获得了灵感,并将 使用经验和现实世界中的实用性来驯服它。
为什么是 D ?
确实,为什么?有谁需要另一种编程语言?
自从 C 语言被发明以来,软件工业走过了一段很长的路。许多新的概念被加入了 C++ 中,但同时维护了同 C 的向后兼容性,包括兼容了原始设计中的所有的弱点。有很多修正这些弱点的尝试,但是兼容性是最大的困扰。同时,C 和 C++ 都在不断引入新的特性。这些新特性必须被小心的加入到现有的结构中,以免重写旧的代码。最终的结果十分复杂—— C 标准将近 500 页,C++ 标准大概有 750 页!C++ 实现起来既困难又代价高昂,造成的结果就是各种实现之间都有差别,因此很难写出完全可以移植的 C++ 代码。
C++ 程序员倾向于使用语言中的孤岛来编程,也就是说,他们倾向于十分精通语言中的某个特性而避免使用其他特性。尽管代码通常在编译器之间是可移植的,但在程序员之间移植就不那么容易了。C++ 的一个长处是它支持很多根本上不同的编程风格——但从长远来看,互相重复和互相冲突的风格会给开发带来妨碍。
C++ 在标准库而不是语言核心中实现了可改变大小的数组和字符串拼接等。不在语言核心中实现这些功能造成了几种不太理想的结果。
是否能把 C++ 的能力释放、重新设计并重铸到一门简单、正交并实用的语言中呢? 这种语言是否能做到易于正确实现,并使编译器有能力有效地生成高度优化的代码呢?
现代编译器技术已经取得了很大的进步,有些原来用作原始编译技术的补充的语言特性已经可以被忽略了(一个这样的例子是 C 语言中的‘register’关键字,一个更为微妙的例子是 C 中的宏预处理程序)。我们可以依赖现代编译器的优化技术而不是使用语言特性(如同原始的编译器所做的那样)来获得可以接受的代码质量。
D的主要目标
- 通过加入已经被证明的能够提高生产力的特性、调整语言特性以避免常见但耗费精力的bug的出现,至少减少软件开发成本10%。
- 是代码易于在编译器之间、在机器之间、在操作系统之间移植。
- 支持多种编程范式,也就是至少支持命令式、结构化、面向对象和范型编程范式。
- 对于熟悉 C 或者 C++ 的人来说,学习曲线要短。
- 提供必要的低级访问能力。
- 要使 D 的编译器从根本上易于实现(相对于 C++ 来说)。
- 要同本机的 C 语言应用程序二进制接口相兼容。
- 语法要做到上下文无关。
- 对编写国际化的应用程序提供便利的支持。
- 同时支持契约式编程和单元测试方法论。
- 能够构建轻量级的、独立的程序。
从C/C++保留而来的特征
粗看上去 D 就像 C 和 C++ 。这样一来学习以及将代码移植到 D 就很容易。从 C/C++ 转向 D 应该很自然。程序员不必从头学起。
使用 D 并不意味着程序员会如 Java 或者 Smalltalk 那样被严格的限制在某一个运行时 vm (虚拟机)上。D 没有虚拟机,编译器直接生成可连接的目标文件。D 如同 C 那样被直接连接到操作系统。通常那些你熟悉的工具如 make 同样适用于 D 的开发。
- D 将很大程度上保留 C/C++ 的 观感 。它将使用相同的代数语法,绝大多数的相同表达式和语句形式,以及总体的结构。
- D 程序既可以采用 C 风格的 函数和数据 范式,也可以采用 C++ 风格的 面向对象 范式,或者它们两者的混合。
- 编译/链接/调试 的开发模型将会被继承下来,但是把 D 编译成为字节码然后解释执行也不会有任何问题。
- 异常处理 越来越多的使用经验显示,异常处理是比 C 传统的“出错代码/全局errno变量”模型更为高级的错误处理模型。
- 运行时类型识别 C++ 部分地实现了这个功能,而 D 更进一步。对运行时类型识别的完全支持将使垃圾收集运行的更好,会使调试器的功能更强,会使对自动持久化的支持更好等等。
- D 维持了同 C 调用惯例 的兼容。这样就能够使 D 程序直接访问操作系统的 API 。程序员有关现有 API 和编程范例的知识和经验可以继续在使用 D 时使用而只需付出很少的努力。
- 运算符重载 D 支持对运算符的重载,这样就可以用用户定义的类型扩展由基本类型构成的类型系统。
- 模板 模板是实现范型编程的一种手段。其他的手段包括使用宏或者采用协变数据类型。使用宏已经过时了。协变类型很直接,但是低效且缺少类型检查。C++ 模板的问题是它们太复杂,同语言的语法不和谐,还有各种各样的类型转换和重载规则,等等。D 提供了一种简单得多的使用模板的方法。
- RAII(资源获得即初始化) RAII 技术是编写可靠软件的重要方法之一。
- Down and dirty 编程 D 将保留 down-and-dirty 编程的能力,而不用采用别的语言编写的外部模块。在进行系统编程时,有时需要将一种指针转换成另一种指针,或者使用汇编语言。D 的目标不是避免 down and dirty 编程,而是减少在进行普通程序设计时对它们的需要。
废弃的特征
- 对 C 的源码级兼容性。保留对 C 的源码级兼容的扩展已经有了(C++ 和 Objective-C)。在这方面的进一步工作受制于大量的遗留代码,已经很难对这些代码进行什么重大的改进了。
- 对 C++ 的链接兼容性。C++ 的运行时对象模型太复杂了——如果要较好的支持它,基本上就是要求 D 编译器变成一个完整的 C++ 编译器了。
- C 预处理程序。宏处理是一种扩展语言的简单方法,它可以给语言加入某些语言本不支持的(对于符号调试器不可见的)特征。条件编译、使用 #include 分层的文本、宏、符号连接等,本质上构成了两种难以区分两种语言的融合体,而不是一种语言。更糟的是(或许是最好的),C 预处理程序是一种十分原始的宏语言。是停下来的时候了,看看预处理程序是用来做什么的,并将这些功能直接设计到语言内部。
- 多重继承。它是一种拥有饱受争议的价值的复杂特征。它很难用一种高效的方式实现,而且在编译器实现它时很容易出现各种 bug 。几乎所有的 MI 的功能都能够通过使用单根继承加接口和聚集的方式实现。而那些只有 MI 才能支持的功能并不能弥补它带来的副作用。
- 名字空间。当链接独立开发的代码时,可能会发生名字的冲突,名字空间就是解决这个问题的一种尝试。模块的概念更简单并且工作得更好。
- 标记名字空间。这是 C 的一个糟糕的特征,结构的标记名称位于一个同其它符号不同的符号表中。C++ 试图合并标记名字空间和正常的名字空间,但同时还要维持对遗留 C 代码的向后兼容性。造成的结果是不可打印。
- 前向声明。C 编译器在语义上只知道什么东西实在词法上位于当前状态之前的。C++ 进行了一点点扩展,类中的成员可以依赖于它之后声明的类成员。D 更进一步,得到了一个合情合理的结论,前向声明根本就没有存在的必要。函数可以按照一种自然的顺序定义,不用再像 C 那样为了避免前向声明而采用常用的从里到外的顺序定义。
- 包含文件。造成编译器运行缓慢的原因之一是编译每个编译单元时都需要重新解析数量巨大的头文件。包含文件的工作应该采用导入到符号表中的方式来完成。
- 在堆栈上创建对象实例。在 D 中,所有的类都通过引用来访问。这样就不需要复制构造函数、赋值运算符、复杂的析构语义以及同异常处理中的堆栈展开的相互作用。内存资源由垃圾收集程序负责释放,其他资源通过使用 D 的 RAII 特征释放。
- 三字节码和双字节码。Unicode 是未来。
- 预处理程序。现代语言不应该需要文本处理,它们应该只需要符号处理。
- 非虚成员函数。在 C++ 中,由累得设计者决定一个函数是否应该是虚函数。在子类中重写一个函数而忘记在父类中将其更新为虚函数是一个常见的(并且非常难以发现的)编码错误。将所有成员函数设置为虚函数,并由编译器来判断函数是否被重写、并由此将没有被重写的函数转换为非虚函数的做法更为可靠。
- 任意长度的位字段。位字段是一种复杂、低效并且很少用到的特征。
- 支持16位计算机。D 从不考虑混合使用远/近指针和其它所有用于声称好的16位代码的机制。D 语言的设计假设目标机器至少拥有32位的平坦内存空间。D 将能够被毫无困难的移植到64位架构上。
- 对编译遍数的互相依赖。在 C++ 中,需要一个符号表和各种的预处理程序命令才能成功的解析一个源文件。这样就使预解析 C++ 源码变得不可能,并且使编写代码分析程序和语法制导的编辑器的过程十分难以正确实现。
- 编译器的复杂性。通过降低实现的复杂度,这就更有可能出现多个正确的实现。
- ‘.’和‘->’之间的区别。这种区别其实很没有必要。‘.’运算符完全可以起到‘->’所起的指针解引用的作用。
D 适合于?
- 经常使用 lint 或者类似的代码分析工具以期在编译之前减少 bug 的程序员。
- 将编译器的警告级别调到最高的人和那些告诉编译器把警告作为错误的人。
- 不得不依靠编程风格规范来避免常见的 C bug 的编程部门经理们。
- 认定 C++ 面向对象编程所允诺的功能由于 C++ 太复杂而不能达到的人。
- 沉溺于 C++ 强大的表达力但是被显式内存管理和查找指针 bug 折磨得精疲力尽的人。
- 需要内建的测试和验证机制的项目。
- 开发百万行规模的程序的团队。
- 认为语言应当提供足够的特征以避免显式处理指针的程序员。
- 编写数值运算程序的程序员。D 拥有众多直接支持数值计算的特征,例如直接提供了复数类型和拥有确定行为的 NaN 和无穷大。(这些都被加进了最新的 C99 标准,但是没有加进 C++ 中。)
- D 的词法分析程序和解析程序完全互相独立,并且独立于语义分析程序。这意味着易于编写简单的工具来很好地处理 D 源码而不用编写一个完整的编译器。这还意味着源码可以以记号的形式传递个某个需要它的程序。
D 不适合于?
- 现实一点说,没人会把上百万行的 C 或 C++ 程序用 D 重新写一遍,因为 D 不直接兼容 C/C++ 源代码,D 并不适合于遗留程序。(但是,D 对遗留的 C API 提供了很好的支持。)
- 非常小的程序——脚本或解释性语言如 Python、DMDScript 或者 Perl 更适合于这种情况。
- 作为第一门程序设计语言—— Basic 或者 Java 更适合于初学者。对于中级到高级的程序员来说,D 是他们优秀的第二门语言。
- 语言纯粹主义者。D 是一门实用的语言,它的每个特征都是为这个目的服务的,D 并没有想成为一门“完美”的语言。例如,D 拥有可以基本上避免在日常任务中使用指针的结构和语义。但是 D 仍然支持指针,因为有时我们需要打破这条规则。类似地,D 保留了类型转换,因为有时我们需要重写类型系统。
模板
D 模板提供了一种提供范型编程和偏特化能力的简洁的方法。
关联数组
关联数组是索引可以为任意类型的数组,不像普通数组那样必须使用整数作为索引。本质上,关联数组就是散列表。关联数组使构建快速、高效、无错的符号表变得容易了。
真正的 typedef
C 和 C++ 的 typedef 实际上是类型 别名 ,因为它不会引入新的类型。D 实现了真正的 typedef :
typedef int handle;
实实在在地创造了一个新类型 handle 。D 同样会对 typedef 引入的类型进行类型检查,并且 typedef 也参与函数重载的决策。例如:
int foo(int i); int foo(handle h);
Bit 类型
机器上最基本的数据类型是位,因此 D 直接支持 bit 数据类型。它在创建位数组时最有用:
bit[] foo;
函数
如你所愿,D 提供常规的对函数的支持,包括全局函数、重载函数、函数在线化、成员函数、虚函数、函数指针等等。另外,D 还支持:
嵌套函数
函数可以嵌套在其他函数内。这对于代码分解、局部性以及函数闭包技术都具有很高的价值。
函数文字量
匿名函数可以直接嵌入到表达式中。
动态闭包
嵌套函数和类成员函数可以被称为闭包(也被称为委托),它们可使范型编程更为容易并保证类型安全。
In, Out 和 Inout 参数
这几个修饰符不只能使函数更为易于理解,还能避免使用指针而不会影响代码的功能,另外这也会提高编译器帮助程序员找到编码问题的可能性。
这些修饰符使 D 能够直接同更多的外部 API 对接。也就无需使用“接口定义语言”(IDL)之类的东西了。
数组
C 数组有几个可以被纠正的缺点:
D 数组有四种变体:指针、静态数组、动态数组和关联数组。
字符串
在 C 和 C++ 中,对字符串的操作是如此的频繁,而又如此的笨拙,以至于最好还是由语言本身来支持它比较好。现代语言都处理字符串连接、复制等等操作,D 语言也提供了这些支持。字符串是直接由经过改进的数组来处理的。
资源管理
垃圾收集
D 的内存分配完全采用垃圾收集。经验告诉我们,C++ 中的很多复杂特征都是用于处理内存释放的。有了垃圾收集,语言就变得简单多了。
有一种看法认为垃圾收集是给那些懒惰、初级的程序员准备的。我还记得那些对 C++ 的评论,毕竟,没有什么 C++ 能做而 C 不能做的,或者这对汇编来说也一样。
采用垃圾收集可以避免 C 和 C++ 中必需的乏味的、易于出错的内存分配和追踪代码。这不只意味着更少的开发时间和更低的维护费用,还意味着程序运行得更快!
当然,可以在 C++ 中使用垃圾收集程序,我已经在我自己的项目中使用它了。C++ 对垃圾收集程序并不友好,这也造成了 C++ 中垃圾收集的低效。很多运行时库的代码都不能同来垃圾收集程序一同工作。
显式内存分配
尽管 D 是一种采用垃圾收集的语言,还是可以重写某个类的 new 和 delete 操作以采用一个定制的分配器。
RAII
RAII 是一种管理资源分配和释放的现代软件开发技术。D 以一种可控的、可预测的方式支持 RAII ,它是独立于垃圾收集程序的回收周期的。
性能
轻量级聚集
D 支持简单的 C 风格的结构,既保证了对 C 数据结构的兼容性,也是因为有时采用类有杀鸡用牛刀之嫌。
内联汇编
设备驱动程序、高性能系统程序、嵌入式系统和某些特殊的代码需要使用汇编语言完成任务。尽管 D 的实现不一定要实现内联汇编,它也仍被定义为语言的一部分。他可以满足绝大多数使用汇编语言的需要,这样就不需要单独的汇编程序或者使用 DLL 了。
许多的 D 实现同时也实现那些类似于 C 的支持 I/O 端口操作、直接访问浮点硬件等内部功能的内函数。
可靠性
现代的语言应该竭尽所能地帮助程序员避免出错。语言提供的帮助有多种形式:从易于使用更为健壮的技术,到有编译器指出明显出错的代码,到运行时检查。
契约
契约式编程(由 B. Meyer 发明)是一种用于保证程序正确性的革命性的技术。D 版本的 DBC 包括函数先验条件、函数后验条件、雷不变量和断言契约。
单元测试
可以给一个类加入单元测试,这样测试程序就能在程序启动时自动运行。这样就能够在每次构建时都验证类是否实现了他所应完成的功能。单元测试构成了源代码的一部分。创建单元测试成为了类开发过程中的自然的一部分,而不是将完成的代码直接抛给测试小组。
单元测试可以使用其它语言完成,但是其结果看起来有一种七拼八凑的感觉,而且你采用的那种语言很可能并不兼容这个概念。单元测试是 D 的一个主要特征。对于库函数来说,单元测试已经被证明是十分有效的。它既可以保证函数工作正常,也可以演示如何使用这些函数。
考虑大量的可以从网上下载的 C++ 库和应用程序代码。其中有“几个”是带有验证测试的?更不要奢望单元测试了?少于 1% ?通常的做法是,如果它们能通过编译,我们就假定它是正确的。而且我们不知道变异过程中给出的警告到底是真正的错误还是瞎唠叨。
契约式编程和单元测试使 D 为编写可信赖、健壮的系统程序的最好的语言。单元测试还是我们能够粗略但快速地估计你从未经手的 D 代码片断的质量——如果没有单元测试和契约式编程,每人会干这种事。
调试特征和语句
现在调试成为了语言语法的一部分了。可以在编译时决定是否使用这些代码,再也不用使用宏或者预处理命令了。调试语法提供了一种持续的、可移植的、易于理解的识别调试代码的方法,使程序员既能够生成带有调试代码的二进制版本,也能够生成没有调试代码的二进制版本。
异常处理
D 采用了更为高级的 try-catch-finally 模型而不是原来的 try-catch 模型。没有必要只是为了利用析构函数实现 finally 语义而构造一个傀儡对象。
同步
因为多线程编程已经越来越成为主流,所以 D 提供了构建多线程程序的原语。同步既可以作用在方法上,也可以作用在对象上。
synchronized int func() { . } 同步方法一次只允许一个线程执行。
同步语句将在语句块周围插入一个互斥体,控制对象或全局的访问。
对健壮性技术的支持
- 使用动态数组而不是指针
- 使用对变量的引用而不是指针
- 使用对对象的引用而不是指针
- 使用垃圾收集而不是显式内存分配
- 内建线程同步原语
- 不再有宏给你的代码来那么一下子
- 使用内联函数而不是宏
- 在很大程度上减少了使用指针的需要
- 整型的大小是明确的
- 不用再担心 char 类型是否有符号了
- 不必再分别在源文件和头文件中重复地写声明了
- 为调试代码提供了显式的解析支持
编译时检查
- 更强的类型检查
- 需要进行显式初始化
- 不允许出现未使用的局部变量
- 不允许出现空的(只由‘;’的)循环体
- 赋值语句不会返回布尔类型的结果
- 废弃过时的 API
运行时检查
- assert() 表达式
- 数组边界检查
- switch 语句中的未定义 case 语句异常
- 内存耗尽异常
- in、out 和类不变量提供了对契约式编程的支持
兼容性
运算符优先级和求值规则
D 保留了 C 的运算符和它们的优先级、求值的规则和类型提升规则。这就避免了由于同 C 的语义不同而造成的微妙的难以发现的 bug 的出现。
直接访问 C API
D 不支拥有同 C 类型对应的类型,它还提供了直接访问 C 函数的能力。完全没有必要编写封装函数和参数变换器,也没有必要逐一地复制聚集类型的成员。
支持所有的 C 数据类型
使对 C API 或者现有的 C 库代码的接口成为可能。D 支持结构、联合、枚举、指针和所有的 C99 类型。D 还拥有设置结构成员对齐方式的能力,这样就可以保证同外部导入的数据格式的兼容。
操作系统异常处理
D 的异常处理机制将在应用程序中利用底层操作系统提供的异常处理方式。
使用现成的工具
D 生成标准的目标文件格式,这样就能够使用标准的汇编程序、链接器、调试器、性能分析工具、可执行程序压缩程序和其他的分析程序,还能够同其他语言编写的代码相链接。
项目管理
版本控制
D 对从同一份源码生成多个版本的程序提供了内建的支持。它替代了 C 预处理程序的 #if/#endif 技术。
废弃
随着代码不停的演进,一些旧的库代码会被更新、更好的版本代替。同时旧的版本必须可用以支持旧的客户代码,旧的版本可以被标记为 废弃的 。可以通过编译器的一个选项来判断使用废弃版本代码的版本是否是合法的,这样一来负责维护的程序员就可以更为轻松的判断哪里是依赖于已经被废弃的特征的。
没有警告
D 编译器不会为可疑的代码生成警告。代码要么可以被编译器接受,要么不能被编译器接受。这样就不会引起有关哪些警告确实是错误而那些不是的争论,也就没有如何处理它们的争论了。对编译器警告的需求本身就是不好的语言设计的表现。
示例程序 (sieve.d)
/* 用Eratosthenes(埃拉托色尼,公元前三世纪的希腊天文学家、数学家和地理学家)筛法求素数 */ bit[8191] flags; int main(){ int i, count, prime, k, iter; printf("10 次迭代\n"); for (iter = 1; iter <= 10; iter++) { count = 0; flags[] = 1; for (i = 0; i < flags.length; i++) { if (flags[i]) { prime = i + i + 3; k = i + prime; while (k < flags.length) { flags[k] = 0; k += prime; } count += 1; } } } printf ("\n%d 个素数", count); return 0;}D vs 其他语言
下表给出了 D 同其他经常提到的语言在各个特征上的粗略的比较。尽管各个语言的标准库中也提供了很多功能,但这张表格只考虑内建到语言核心中的特征。我们只考虑官方的标准特征,不考虑被提议的特征、测试版的特征或者非标准的扩展特征。而且,如同所有其他的语言比较一样,提到什么特征、忽略什么特征以及我对这些特征的解释都是带有偏向性的。
| 特性 | D | C | C++ | C# | Java |
|---|
| 垃圾收集 | Yes | No | No | Yes | Yes |
| 函数 | | | | | |
|---|
| 函数委托 | Yes | No | No | Yes | No |
| 函数重载 | Yes | No | Yes | Yes | Yes |
| 传出(out)函数参数 | Yes | Yes | Yes | Yes | No |
| 嵌套函数 | Yes | No | No | No | No |
| 函数文字量 | Yes | No | No | No | No |
| 动态闭包 | Yes | No | No | No | No |
| 数组 | | | | | |
|---|
| 轻量数组 | Yes | Yes | Yes | No | No |
| 变长数组 | Yes | No | No | No | No |
| 位数组 | Yes | No | No | No | No |
| 内建字符串 | Yes | No | No | Yes | Yes |
| 数组切片 | Yes | No | No | No | No |
| 数组边界检查 | Yes | No | No | Yes | Yes |
| 关联数组 | Yes | No | No | No | No |
| 强类型定义 typedef | Yes | No | No | No | No |
| 字符串 switch 语句 | Yes | No | No | Yes | No |
| 别名 | Yes | Yes | Yes | No | No |
| OOP | | | | | |
|---|
| 面向对象 | Yes | No | Yes | Yes | Yes |
| 多重继承 | No | No | Yes | No | No |
| 接口 | Yes | No | Yes | Yes | Yes |
| 运算符重载 | Yes | No | Yes | Yes | No |
| 模块 | Yes | No | Yes | Yes | Yes |
| 动态类装入 | No | No | No | Yes | Yes |
| 嵌套类 | Yes | Yes | Yes | Yes | Yes |
| 内部(适配器)类 | No | No | No | No | Yes |
| 协变返回型别 | Yes | No | Yes | No | No |
| 属性 | Yes | No | No | Yes | No |
| 性能 | | | | | |
|---|
| 内联汇编 | Yes | Yes | Yes | No | No |
| 直接访问硬件 | Yes | Yes | Yes | No | No |
| 轻量对象 | Yes | Yes | Yes | Yes | No |
| 显示内存分配控制 | Yes | Yes | Yes | No | No |
| 独立于虚拟机 | Yes | Yes | Yes | No | No |
| 直接生成本地代码 | Yes | Yes | Yes | No | No |
| 泛型编程 | | | | | |
|---|
| 模板 | Yes | No | Yes | No | No |
| 混入 | Yes | No | No | No | No |
| typeof | Yes | No | No | Yes | No |
| foreach | Yes | No | No | Yes | No |
| 约束 | Yes | No | No | No | No |
| 可靠性 | | | | | |
|---|
| 契约式编程 | Yes | No | No | No | No |
| 单元测试 | Yes | No | No | No | No |
| 静态构造顺序 | Yes | No | No | Yes | Yes |
| 确保初始化 | Yes | No | No | Yes | Yes |
| RAII(自动析构函数) | Yes | No | Yes | Yes | No |
| 异常处理 | Yes | No | Yes | Yes | Yes |
| try-catch-finally 基本块 | Yes | No | No | Yes | Yes |
| 线程同步原语 | Yes | No | No | Yes | Yes |
| 兼容性 | | | | | |
|---|
| C风格语法 | Yes | Yes | Yes | Yes | Yes |
| 枚举型别 | Yes | Yes | Yes | Yes | No |
| 支持所有 C 型别 | Yes | Yes | No | No | No |
| 80 位浮点数 | Yes | Yes | Yes | No | No |
| 负数和虚数 | Yes | Yes | No | No | No |
| 直接访问 C | Yes | Yes | Yes | No | No |
| 使用现有的调试器 | Yes | Yes | Yes | No | No |
| 结构成员对齐控制 | Yes | No | No | No | No |
| 生成标准目标文件 | Yes | Yes | Yes | No | No |
| 宏文本预处理器 | No | Yes | Yes | No | No |
| 其他 | | | | | |
|---|
| 条件编译 | Yes | Yes | Yes | Yes | No |
| Unicode源代码 | Yes | Yes | Yes | Yes | Yes |
注记
- C 语言规范
- ANSI/ISO/IEC 9899-1999 (a.k.a. C99)
- C++ 语言规范
- ISO/IEC 14882-1998 (a.k.a. C++98)
- C# 语言规范
- ECMA-334
- Java 语言规范
- "The Java Language Specification" by Gosling, Joy, and Steele, Addison-Wesley, ISBN 0-201-63451-1
- 面向对象
- 这意味着支持类、成员函数、继承和虚函数分派。
- 内联汇编
- 许多 C 和 C++ 编译器支持内联汇编,但这并不是语言标准的一部分,并且各种实现的语法和质量都有很大差别。
- 接口
- C++ 对接口的支持是如此不完善,以至于专门发明了 IDL(接口定义语言)弥补这个不足。
- 模块
- 许多人指出 C++ 并不真正支持模块,他们是对的。但是 C++ 的名字空间和头文件一起可以实现模块的许多特征。
- 垃圾收集
- Hans-Boehm 垃圾收集器可以成功的用于 C 和 C++ ,但是这并不是语言标准的一部分。
- 契约式编程
- Digital Mars 的 C++ 编译器的一个扩展支持 契约式编程 。可以对比一下实现契约式编程的 C++ 技术 和 D 支持的契约式编程。
- 变长数组
- C++ 标准库中有一部分是用于实现变长数组的,但是,它们并不是核心语言的一部分。一个符合标准的独立的 C++(C++98 17.4.1.3)实现并不必须提供这些库。
- 内建字符串
- C++ 标准库中有一部分是用于实现字符串的,但是,它们并不是核心语言的一部分。一个符合标准的独立的 C++(C++98 17.4.1.3)实现并不必须提供这些库。这有关于 C++ 字符串和 D 内建字符串的一个 比较 。
- 强类型定义 typedef
- 强类型定义 typedef 在 C/C++ 中可以通过将一个类型用一个结构封装来模拟。如果要采用这种方式,会使编程工作十分繁琐,所以它并不被人支持。
- 使用现有的调试器
- 采用这种方法意味着使用那些能够处理嵌入到常见可执行文件格式中的调试数据的常见调试器。不必编写一个只能用于某种语言的调试程序。
- 结构成员的对齐控制
- 尽管许多的 C/C++ 编译器包括指定结构对齐方式的编译器指令,但对此并没有一个标准,而不同编译器的做法通常是不兼容的。关于结构成员对齐,C# 标准 ECMA-334 25.5.8 只提到:“成员在结构中的顺序是未指定的。出于对齐的目的,可以在结构的开头、中间或者结尾处加入匿名填充。填充值是未定义的。”因此,尽管 Microsoft 可能进行扩展以支持特定的成员对齐方式,但它们不是 C# 官方标准的一部分。
- 支持所有 C 类型
- C99 添加了 C++ 部支持的许多新类型。
- 80 位浮点数
- 尽管 C 和 C++ 标准规定了 long double 类型,很少有编译器(除了 Digital Mars C/C++)真正实现了 80 位(或更长)的浮点类型。
- 混入
- 在不同的程序设计语言中,混入有许多不同的意义。D 的混入 是指将任意的声明序列插入(混入)到当前作用域中。混入可以在全局、类、结构或者局部级别使用。
- C++ 混入
- C++ 混入指的是另一套技术。其一同 D 的接口类是同类的东西。其二是指创建一个下述形式的模板:
template <class Base> class Mixin : public Base { ... 混入过程体 ... }D 的混入与此不同。
- 同 Ada 的比较
- James S. Rogers 编绘了 同 Ada 的比较图 。
- 内部(适配器)类
- 嵌套类 是定义在另一个类作用域内的类。内部类 是能够引用外部类的成员和域的嵌套类;你可以把它们看作是拥有指向包含它们的外部类的‘this’指针的类。
Trackback: http://tb.donews.net/TrackBack.aspx?PostId=1006978