D语言 技术专题简介-编程知识网

简介

D语言编程范型多范型:函数式、指令式、面向对象设计者Walter Bright,Andrei Alexandrescu发行时间2001年12月8日,​21年前​(2001-12-08)当前版本2.101.1 (2022年12月15日;稳定版本) 类型系统静态强类型网站dlang.org 主要实现产品DMD、GDC、LDC启发语言C、C++、C#、Java、EiffelD语言是一种编程语言,具备多范型,例如面向对象、指令式。由沃尔特·布莱特和安德烈·亚历山德雷斯库所开发,起源自C++,深受C++的影响,然而其不是C++的变种,而是重新设计来自C++的部分特性,并受到其它编程语言观念的影响,如Java、C#以及Eiffel。2007年1月2日发布1.0稳定版本。2007年1月17日发布2.0版本。Walter Bright本身是Symantec C++编译器的作者,另一名作者Andrei Alexandrescu是Facebook的研究科学家,他与一个团队用D语言重写一些Facebook的重要操作。

特性

D的设计来自实际的C++用法的经验教训,而不是从理论的角度。D沿用了很多C/C++观念,同时摒弃了一些概念,因此D并不完全兼容C/C++代码。D实现了C++的功能,实现了契约式设计(design by contract)、单元测试、真正的模块性、自动化存储器管理(垃圾回收)、头等数组(first class array)、关联数组、动态数组、数组切片、嵌套函数(嵌套函数)、内部类别、闭包的限制形式、匿名函数、编译时期函数执行、惰性计算以及革新的模板语法。D保有C++的性能以进行低端程序设计,并加入完整的内联汇编器支持。C++的多重继承改以Java 单继承与接口混合的风格取代。D的声明、语句和表达式语法(英语:Syntax (programming languages))几乎和C++一样。

内联汇编器(inline assembler)象征了D和Java、C#等应用程序语言的不同。内联汇编器让程序员输入机器特定的汇编语言码,如同标准D代码—通常由系统程序员使用的技术,以访问处理器的低端功能,直接以硬件下的界面执行程序,如操作系统以及驱动程序。

D内置支持文件注解,不过目前为止,只有Digital Mars实现版本有提供文件产生器。

程序设计范型

D支持五种主要的程序设计泛型—指令式、面向对象以及元编程、函数式和并发(演员模型)。

指令式

命令式编程几乎和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直接编译成原生码以高效执行。

D语言1.x版本已稳定,不再功能变更或扩展,2.0版本是其正式版本,不完全兼容旧版本的语言和编译器。官方编译器由Walter Bright定义语言本身。

DMD编译器(页面存档备份,存于互联网档案馆):Digital Mars D编译器,由Walter Bright编写的官方D编译器。编译器前端的授权许可为Artistic License和GNU GPL两者;前端的源代码连同编译器执行码一起发布。编译器的后端则是私有的。

GDC(页面存档备份,存于互联网档案馆):D 1.0编译器,以DMD编译器前端,以及GCC后端所组成。

LDC(页面存档备份,存于互联网档案馆):D 2.0编译器,以DMD编译器前端,以及LLVM后端所组成。LDC的官方版本已不支持D1.0,但其分支版本依然支持D1.0。

问题和争议

此章节没有提供参考来源,内容可能无法查证(2020年4月27日)

运算符重载

D运算符重载在一定程度上不如C++强大。简单的例子是opIndex,它不允许返回引用。这使像是obj = 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表示。新版本中定义stringchar的别名,不过别名定义必须与旧版本兼容。

import std.stdio;       // 以使用writefln()alias char string;    // 以相容旧的编译器;新的编译器中已隐含定义int main(string args){    foreach(i, a; args)        writefln("args = '%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  container;    // 将人们加入到容器中,并让他们携带一些项目    container ~= "scarf";    container ~= "tickets";    container ~= "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);                       // 支援OOPwritefln(cl.argnum, cl.suffix, " arg: %s", cl.argv);    // 使用者定义的类别属性。delete cl;               // 垃圾回收或显示的记忆体管理——由你自己选择    }    // 巢状结构、类别和函式    struct specs    {        // 所有的变数会在执行时期自动初始化为0        int count, allocated; // 不过你可选择避开阵列的初始化        int 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).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.");