D语言
D语言是一种编程语言,具备多范型,例如面向对象、指令式。由沃尔特·布莱特和安德烈·亚历山德雷斯库所开发,起源自C++,深受C++的影响,然而其不是C++的变种,而是重新设计来自C++的部分特性,并受到其它编程语言观念的影响,如Java、C#以及Eiffel。2007年1月2日发布1.0稳定版本。2007年1月17日发布2.0版本。
![]() | |
多范型:函数式、指令式、面向对象 | |
设计者 | Walter Bright,Andrei Alexandrescu |
2001年12月8日[1] | |
当前版本 |
|
型态系统 | 静态强类型 |
网站 | |
主要实作产品 | |
DMD、GDC、LDC | |
启发语言 | |
C、C++、C#、Java、Eiffel |
Walter Bright本身是Symantec C++编译器的作者,另一名作者Andrei Alexandrescu是Facebook的研究科学家,他与一个团队用D语言重写一些Facebook的重要操作。[3]
特性
D的设计来自实际的C++用法的经验教训,而不是从理论的角度。D沿用了很多C/C++观念,同时摒弃了一些概念,因此D并不完全兼容C/C++代码。D实现了C++的功能,实现了契约式设计(design by contract)、单元测试、真正的模块性、自动化内存管理(垃圾回收)、头等数组(first class array)、关联数组、动态数组、数组切片、嵌套函数(嵌套函数)、内部类别、闭包的限制形式、匿名函数、编译时期函数运行、惰性计算以及革新的模板语法。D保有C++的性能以进行低级编程,并加入完整的内联汇编器支持。C++的多重继承改以Java 单继承与接口混合的风格取代。D的、语句和表达式语法几乎和C++一样。
内联汇编器(inline assembler)象征了D和Java、C#等应用编程语言的不同。内联汇编器让程序员输入机器特定的汇编语言码,如同标准D代码—通常由系统程序员使用的技术,以访问处理器的低级功能,直接以硬件下的界面运行程序,如操作系统以及驱动程序。
D内置支持文档注解,不过目前为止,只有Digital Mars实作版本有提供文档产生器。
指令式
命令式编程几乎和C一样。函数、数据、语句、声明以及表达式的运作就如同C一般,且可直接访问C运行时期程序库。
面向对象
在D里面的面向对象编程,是以单继承分层结构,配合所有类别衍伸自类别对象为基础。多重继承可使用界面(界面很像C++的抽象类别)。
元编程
以模板组合、编译时期函数运行、多元组以及字符串混合来支持元编程。
内存管理
内存通常以垃圾回收管理,不过当这些对象超出作用域时,可立即结束指定的对象。还是可以使用重载操作符new和delete,以及简单的直接调用C的malloc函数和free函数以进行显示的内存管理。垃圾回收可禁用个别的对象或事件,以健全整个程序,如果在内存管理上有更多的控制,则更为理想。当垃圾回收在程序中有所不足时,手册还提供许多如何实作不同的高度优化内存管理方案的范例。
与其它系统的相互作用
支持C的应用程序二进制接口(ABI),以及C的基本和衍伸型态,就能直接访问现有的C代码以及程序库。C的标准函数库也是D标准的一部分。除非你使用非常清楚的命名空间,它可以稍微散乱的访问,因为它散布遍及于D模块—不过纯粹的D标准函数库也通常够用,除非要与C代码接合。
并未完整支持C++的ABI,尽管D可以访问写给C ABI的C++代码,且可访问C++COM(组件对象模型)代码。D语法分析器了解外部(C++)调用约定,以链接C++对象,不过它只实作在D 2.0。
D 2.0
D 2.0,D 新一代版本,D2.0与D1.0是不兼容的,类似Python2和Python3的区别。目前D2已经稳定下来。其中一部分特性包括支持强制常数正确性(const-correctness),以及有限的支持链接以 C++ 编写的代码。
问题和争议
操作符重载
D操作符重载在一定程度上不如C++强大。简单的例子是opIndex,它不允许返回引用。这使像是obj[i] = 5;的赋值不可能存在。D的解决方法是opIndexAssign操作符,它只用于这种特殊情况。此外,C++返回参考的方法允许返回型态的重载赋值操作符的用法。这在目前的D还不可能做到。D 2.0将会引入opIndexLvalue修正 - 类似操作符重载和opIndexAssign。
低功的结构
结构在D之中是一种朴素旧式数据的型态,不过也可像变量一样包含方法。这对有意轻量化的建构而言相当实用,如矩阵或矢量,这些不需要完整的D类别功能(以及体积)。然而,D结构没有建构子和解构子。建构子可用静态opCall操作符部分取代,不过它没有适合的解构子等价物。此外,结构不允许继承,这会是有益的设计,如诡异循环模板模式(curiously recurring template pattern)的使用。
标准函数库中缺乏功能
D的标准函数库称作Phobos(页面存档备份,存于),且时常被认为过分简单。tango(页面存档备份,存于)项目编写另一个标准函数库试图修正这一部分,不过phobos和tango目前由于不同的对象类别实作(导致垃圾回收困难)而互不兼容。存在两种事实上的标准函数库可能导致更大的问题,部分软件使用phobos,而其它软件使用tango。
缺乏明确的目标
D经常限于「修正并改进的C++」。这会导致过分强调功能,这起因于加入新功能只是因为他们认为有用。举个例子,
未完成对共享/动态函数库的
Unix的ELF共享函数库使用GDC编译器到某个程度。在Windows系统中,目前还不支持DLL。因此现阶段不可能编写插件。不像C++,经由C函数发送的D对象将不能运作,因为这将会与垃圾回收器产生冲突。
范例
范例1
这个范例程序会输出它自己的命令行参数。main函数是D程序的进入点,args是表示为字符串数组的命令行参数。在D语言里的字符串是一个字符数组,以char[]表示。新版本中定义string为char[]的别名,不过别名定义必须与旧版本兼容。
import std.stdio; // 以使用writefln()
alias char[] string; // 以兼容旧的编译器;新的编译器中已隐含定义
int main(string[] args)
{
foreach(i, a; args)
writefln("args[%d] = '%s'", i, a);
return 0;
}
foreach语法可迭代所有的集合,在本例中,它从args数组生成索引(i)和值(a)的串行。索引i和值a的型态会从args数组的型态推断。
范例2
本例使用关联数组创建更复杂的数据结构。
import std.stdio; // 以使用writefln()
alias char[] string; // 以兼容旧的编译器;新的编译器中已隐含定义
int main(string[] args)
{
// 声明以字符串键和字符串数组作为数据的关联数组
string[] [string] container;
// 将人们加入到容器中,并让他们携带一些项目
container["Anya"] ~= "scarf";
container["Dimitri"] ~= "tickets";
container["Anya"] ~= "puppy";
// 迭代容器中所有的人
//Iterate over all the persons in the container
foreach (string person, string[] items; container)
display_item_count(person, items);
return 0;//完成
}
void display_item_count(string person, string[] items)
{
writefln(person, " is carrying ", items.length, " items.");
}
范例3
本例繁多的注解显示出D语言与C++ 的不同之处,以及仍然保留的方面。
#!/usr/bin/dmd -run
/* 支持sh风格的script语法!*/
/* D语言的Hello World
* 进行编译:
* dmd hello.d
* 或进行优化:
* dmd -O -inline -release hello.d
* 或产生文档:
* dmd hello.d -D
*/
import std.stdio; // 参照常用的I/O例行工作。
alias char[] string; // 以兼容旧的编译器;新的编译器中已隐含定义
int main(string[] args)
{
// 'writefln' (写入-格式化-行,Write-Formatted-Line)即型态安全的「printf」
writefln("Hello World, " // 自动链接的字符串文本
"Reloaded");
// 字符串即字符的动态数组「char[]」,别名为「string」
// 自动的型态推断,以及内置的foreach
foreach(argc, argv; args)
{
auto cl = new CmdLin(argc, argv); // 支持OOP
writefln(cl.argnum, cl.suffix, " arg: %s", cl.argv); // 用户定义的类别属性。
delete cl; // 垃圾回收或显示的内存管理——由你自己选择
}
// 嵌套结构、类别和函数
struct specs
{
// 所有的变量会在运行时期自动初始化为0
int count, allocated;
// 不过你可选择避开数组的初始化
int[10000] bigarray = void;
}
specs argspecs(string[] args)
// 可选用的(内置)函数契约。
in
{
assert(args.length > 0); // 内置assert
}
out(result)
{
assert(result.count == CmdLin.total);
assert(result.allocated > 0);
}
body
{
specs* s = new specs;
// 不需要「->」
s.count = args.length; // 「length」属性是元素的数量。
s.allocated = typeof(args).sizeof; // 原生型态内置的属性
foreach(arg; args)
s.allocated += arg.length * typeof(arg[0]).sizeof;
return *s;
}
// 内置字符串和普通的字符串操作,例如「~」是链接。
string argcmsg = "argc = %d";
string allocmsg = "allocated = %d";
writefln(argcmsg ~ ", " ~ allocmsg,
argspecs(args).count,argspecs(args).allocated);
return 0;
}
/**
* 保存单独命令行参数
*/
class CmdLin
{
private
{
int _argc;
string _argv;
static uint _totalc;
}
public:
/**
* 对象的建构子。
* 参数:
* argc = 参数的串行计数。
* argv = 参数内文。
*/
this(int argc, string argv)
{
_argc = argc + 1;
_argv = argv;
_totalc++;
}
~this() // 对象的解构子
{
// 本例中不做任何事。
}
int argnum() // 属性,可返回参数数目
{
return _argc;
}
string argv() // 属性,可返回参数内文
{
return _argv;
}
wstring suffix() // 属性,可返回序数后缀
{
wstring suffix; // 内置Unicode字符串(UTF-8,UTF-16,UTF-32)
switch(_argc)
{
case 1:
suffix = "st";
break;
case 2:
suffix = "nd";
break;
case 3:
suffix = "rd";
break;
default: // 'default' is mandatory with "-w" compile switch.
suffix = "th";
}
return suffix;
}
/**
* 静态属性,如同在C++ 或Java中,
* 适用于类别对象,而不是实体。
* 返回:己加入的命令行参数总数。
*/
static typeof(_totalc) total()
{
return _totalc;
}
// 类别不变量,任何方法在运行之后,这些必须为真。
invariant ()
{
assert(_argc > 0);
assert(_totalc >= _argc);
}
}
范例4
本例显示出一部分D语言强大的编译时期特性。
/*
* D语言里的模板比C++ 的要更加强大。
* 在此可以看到使用static if(D的编译时期条件建构)简单的建构出阶乘模板。
*/
template Factorial(ulong n)
{
static if( n <= 1 )
const Factorial = 1;
else
const Factorial = n * Factorial!(n-1);
}
/*
* 这里有一个正规的函数,可完成同样的计算。
* 注意它们有多么的相似。
*/
ulong factorial(ulong n)
{
if( n <= 1 )
return 1;
else
return n * factorial(n-1);
}
/*
* 终于,我们可以计算我们的阶乘。注意,我们不需要去
* 明确的指定我们的常数的型态:编译器有足够的智能为
* 我们填充空白,因为它早已知道赋值中右手边的型态。
*/
const fact_7 = Factorial!(7);
/*
* 这是编译时期函数评估的范例:普通函数可用于常数、
* 编译时期表达式,假若它们满足一定的条件。
*/
const fact_9 = factorial(9);
/*在此我们可以看到多么强大的D我们使用
* std.metastrings.Format模板完成型态安全的printf
* 数据格式化,并使用message pragma显示计算结果。
*/
import std.metastrings;
pragma(msg, Format!("7! = %s", fact_7));
pragma(msg, Format!("9! = %s", fact_9));
/*
* 完成任务后,我们可以强制停止编译。这样的程序需是
* 从未实际编译成可运行档!
*/
static assert(false, "My work here is done.");
参考数据
- . D Programming Language 1.0. Digital Mars. [1 December 2011]. (原始内容存档于2019-03-11).
- . [2024年4月2日].
- Metz, Cade. . Wired.com. 《连线》杂志. 2014年7月7日 [2014年7月27日]. (原始内容存档于2014年7月26日).
Today, Alexandrescu is a research scientist at Facebook, where he and a team of coders are using D to refashion small parts of the company’s massive operation.
- . [28 August 2012]. (原始内容存档于2013-08-24).
外部链接
![]() |
维基教科书中的相关电子:Programming:D |
- Digital Mars: D编程语言(页面存档备份,存于)(官方网站)
- 开放式目录计划中和D相关的内容
- DSource,D语言的开放原代码社群。(页面存档备份,存于)
- Dprogramming.com,窗口化程序库DFL的首页。(页面存档备份,存于)
- Wiki4D,「D语言的维基页」(页面存档备份,存于)
- gdc(页面存档备份,存于),GCC的D语言前端
- 电脑语言评测游戏
- D文档的维基页
- D语言特性列表(页面存档备份,存于)
- Walter Bright介绍D语言的视频(页面存档备份,存于)
- Ddbg - Win32 D调试器
- DWin - D语言库
- DLogo - D语言按钮,广告栏(页面存档备份,存于)
- SciTE4D - D语言编辑器*D语言中国社区, D语言入门,D语言GUI介绍等(页面存档备份,存于)
- D语言中文讨论区
- D Programming Language简介
- D语言教学 (页面存档备份,存于)