前言:人类社会的进步是建立在对旧有事物的不断改造、优化和创新中的,计算机技术也如此,哪怕是一次小的改进都能给业界带来飞跃和影响,推动技术的前进。也有更多的人愿意参与这个领域并贡献智慧,才得以使新技术和新语言才得以不断涌现,比如最近的J2EE WEB开发中的EasyJWEB MVC框架的出现,D语言的诞生等。对语言极感兴趣的我对D语言进行了了解和使用,D语言做为一门通用的系统、应用编程的高级语言,它能生成高效率高性能的代码并可以直接对硬件和操作系统API进行访问,同时借鉴了C、C++、JAVA、C#和其他一些动态语言的优点,支持面向对象编程,同时用独立进程进行垃圾回收,使D语言开发的程序更高效,而且能满足对编译器改造以求程序有最高性能的开发人员的需求。本文对D语言只是进行一个初步的介绍,适合于对D语言感兴趣的新手或者是D语言的初学者以及想了解D语言的人,如要学习请通过其他方式进一步了解
一、D语言的诞生及概念
D语言是由Symantec C++编译器的作者Walter Bright于1999年底酝酿,并在2004年发布的一门适合与通用系统和通应用编程高级语言。D语言借鉴了C、C++、JAVA、C#等和其他一些动态语言的优点,也集成了数十年来优秀编译器经验,其目的是编写从中等规模到那些由团队合作完成、数百万行代码规模的各种程序。D语言也具有良好的错误处理,垃圾回收和内存管理机制。
二、D语言的特性
D语言集成了其他优秀语言尤其是C++的特性,也有了它自己的一些新特性。下面来看看D语言的一些特性:
1.垃圾回收
D语言的垃圾收集器并不同于Java和C#的垃圾回收。它的垃圾回收器并不依赖于虚拟机,而是作为可执行程序单独来运行的。这样它的效率非常高。这也意味着这个类似C++语法的计算机语言再也不用释放内存了,只要按需来分配内存即可。
2.动态改变数组大小
D语言保留了动态数组的支持
3.数组边界检查
检查数组边界,防止数组越界而导致程序出现各种意外
4.关联数组
关联数组的下标不一定非得是一个数组,因而有广泛的用途,关联数组是索引可以为任意类型的数组,不像普通数组那样必须使用整数作为索引。本质上,关联数组就是散列表。关联数组使构建快速、高效、无错的符号表变得容易了
5. 混入
在D中,一个混入从一个模版声明的过程体内提取一个任意的声明集合,并将它们插入到当前的上下文中
6.面向对象
D 语言的面向对象天性来自于类。采用的继承模型时单根继承加接口。Object 类为与继承体系的最顶端,所以所有的类都实现了一个通用的功能集合。类通过引用的方式实例化,所以不需要用于在异常后进行清理工作的复杂代码。
7.接口编程
8.操作符重载
类可以通过重载现有的运算符扩展类型系统来支持新类型。例如创建一个 bignumber class ,然后重载 +、-、* 和 / 运算符,这样大数类就可以使用普通的代数运算语法了
9.模块
源文件同模块是一一对应的。D 不再“包含”带有声明的文件的文本,而是使用关键字import“导入”该模块。不用担心多次导入一个模块,也不用再把头文件用 #ifndef/#endif 或者 #pragma once 包起来了
10.嵌套类
11.属性
12.嵌入汇编语言
设备驱动程序、高性能系统程序、嵌入式系统和某些特殊的代码需要使用汇编语言完成任务。尽管 D 的实现不一定要实现内联汇编,它也仍被定义为语言的一部分。他可以满足绝大多数使用汇编语言的需要,这样就不需要单独的汇编程序或者使用 DLL 了。
13.直接访问硬件
许多的 D 实现同时也实现那些类似于 C 的支持 I/O 端口操作、直接访问浮点硬件等内部功能的内函数。
14.模板
D语言提供提供了一种提供范型编程和偏特化能力的简洁的方法
15..契约
契约式编程(由 B. Meyer 发明)是一种用于保证程序正确性的革命性的技术。D 版本的 DBC 包括函数先验条件、函数后验条件、雷不变量和断言契约。
16.RAII
资源获得即初始化,是编写可靠软件的重要方法之一
17.内存管理
可以重写某个类的 new 和 delete 操作以采用一个定制的分配器
18. 单元测试
可以给一个类加入单元测试,这样测试程序就能在程序启动时自动运行。这样就能够在每次构建时都验证类是否实现了他所应完成的功能。单元测试构成了源代码的一部分。创建单元测试成为了类开发过程中的自然的一部分,而不是将完成的代码直接抛给测试小组。
19.同步
因为多线程编程已经越来越成为主流,所以 D 提供了构建多线程程序的原语。同步既可以作用在方法上,也可以作用在对象上。
20.操作系统异常处理
D 的异常处理机制将在应用程序中利用底层操作系统提供的异常处理方式
21.版本控制
D语言支持从同一套代码构建多种版本和各种调试级别的程序
22.对健壮性技术的支持
使用动态数组而不是指针
使用对变量的引用而不是指针
使用对对象的引用而不是指针
使用垃圾收集而不是显式内存分配
内建线程同步原语
不再有宏给你的代码来那么一下子
使用内联函数而不是宏
在很大程度上减少了使用指针的需要
整型的大小是明确的
不用再担心 char 类型是否有符号了
不必再分别在源文件和头文件中重复地写声明了
为调试代码提供了显式的解析支持
23.编译时检查
更强的类型检查
需要进行显式初始化
不允许出现未使用的局部变量
不允许出现空的(只由‘;’的)循环体
赋值语句不会返回布尔类型的结果
废弃过时的 API
24.运行时检查
assert() 表达式
数组边界检查
switch 语句中的未定义 case 语句异常
内存耗尽和内存泄露异常
in、out 和类不变量提供了对契约式编程的支持
三、D语言的主要目标和适合学习的群体
1.D语言的主要目标
通过加入已经被证明的能够提高生产力的特性、调整语言特性以避免常见但耗费精力的bug的出现,至少减少软件开发成本10%。
是代码易于在编译器之间、在机器之间、在操作系统之间移植。
支持多种编程范式,也就是至少支持命令式、结构化、面向对象和范型编程范式。
对于熟悉 C 或者 C++ 的人来说,学习曲线要短。
提供必要的低级访问能力。
要使 D 的编译器从根本上易于实现(相对于 C++ 来说)。
要同本机的 C 语言应用程序二进制接口相兼容。
语法要做到上下文无关。
对编写国际化的应用程序提供便利的支持。
同时支持契约式编程和单元测试方法论。
能够构建轻量级的、独立的程序。
2.D语言比较适合的使用群体
经常使用 lint 或者类似的代码分析工具以期在编译之前减少 bug 的程序员。
将编译器的警告级别调到最高的人和那些告诉编译器把警告作为错误的人。
不得不依靠编程风格规范来避免常见的 C bug 的编程部门经理们。
认定 C++ 面向对象编程所允诺的功能由于 C++ 太复杂而不能达到的人。
沉溺于 C++ 强大的表达力但是被显式内存管理和查找指针 bug 折磨得精疲力尽的人。
需要内建的测试和验证机制的项目。
开发百万行规模的程序的团队。
认为语言应当提供足够的特征以避免显式处理指针的程序员。
编写数值运算程序的程序员。D 拥有众多直接支持数值计算的特征,例如直接提供了复数类型和拥有确定行为的 NaN 和无穷大。(这些都被加进了最新的 C99 标准,但是没有加进 C++ 中。)
D 的词法分析程序和解析程序完全互相独立,并且独立于语义分析程序。这意味着易于编写简单的工具来很好地处理 D 源码而不用编写一个完整的编译器。这还意味着源码可以以记号的形式传递个某个需要它的程序。
四、D语言开发环境的配置和运行第一个D语言程序
打开
http://www.digitalmars.com/d/dcompiler.html,下载其中的dmd.zip和dmc.zip, dmd.2.007.zip尚在测试之中,暂不建议初学者下载。下载好之后分别分别解压到e:\devtools\dmd和e:\devtools\dmc中,然后把e:\devtools\dmc\dm文件夹下的文件覆盖到e:\devtools\dmd\dm中,然后把e:\devtools\dmd\bin和e:\devtools\dmd\dm\bin加入到环境变量的PATH中,这样我们就可以在命令行下对D语言编写的代码进行编译了,如果需要链接我们还需要做如下配置:
设置环境变量LIB分别为e:\devtools\dmd\lib和e:\devtools\dmd\dm\lib,
设置环境变量LINKCMD为e:\devtools\dmd\dm\bin
并编辑e:\devtools\dmd\bin\sc.ini为如下内容
[Environment]
LIB="e:\devtools\dmd\lib";"e:\devtools\dmd\dm\lib"
DFLAGS=" e:\devtools\dmd\src\phobos"
LINKCMD="e:\devtools\dmd\dm\bin"
DDOCFILE=mysettings.ddoc
这样整个D语言的编译链接就配置完了
void main ()
{
printf ("Hello easyjf!\n");
}
并保存为e:\mysrc\helloejf.d
然后进入命令提示符运行dmd e:\mysrc\helloejf.d即可编译出helloejf.exe,运行helloejf.exe如果出现Hello easyjf!表示你的第一个D语言程序运行成功
当然现在的这个helloejf.exe程序是离不开D语言环境的,如果要真正成为独立的exe程序我们必须对它进行build,同样在e:\mysrc\目录下我们建立一个helloejf.brf的文件,然后我们在命令提示符下进入e:\mysrc\运行build @helloejf就可以产生真正的helloejf.exe文件,而且我们可以在不需要D语言环境的其他计算机上运行。
五、D语言程序示例
运行以下代码时请保存为unicode编码格式
1.D语言的声明、类型和属性:
import std.stdio; // 引入D语言的标准io库, 它是"dmdlibphobos.lib"的一部分; 库文件的查找由sc.ini中的LIB环境变量指定.
void main()
{
try
{
tryDeclarations();
tryTypes();
tryProperties();
}
catch(Exception e)
{
writefln("There is an error:");
}
}
//参考"dmdhtmlddeclaration.html"
void tryDeclarations()
{
// D语言中的变量在会初始化为默认值
int myInt;
uint myUint;
float myFloat;
writefln("default of int = ",myInt,
", default of uint = ", myUint,
", default of float = ", myFloat);
// 声明是从右向左读的
writef("This is an array : ");
int[4] myArray;
foreach(int i,int e;myArray)//如果下标越界,则会抛出一个Exception
writef(i, " = ", e,", ");
writefln();
//auto关键字
auto myAuto = 99;
writefln("this is an auto var, sizeof = ", myAuto.sizeof,
", value = ", myAuto);
typedef int MYINT; //typedef是强类型的
alias int MYINITA; //alias对于类型系统则与原来的类型相同
}
//参考"dmdhtmld ype.html"
void tryTypes()
{
//enum可以指定数据类型
enum ETest : ubyte
{
ET_One, ET_Two
}
//使用cast关键字进行显示类型转换
int a = -1;
uint b = cast(uint)a;
writefln("b = ", b);
//function关键字用来声明函数指针
void function(/**//*参数列表*/) myFunctionPtr;
myFunctionPtr = &tryDeclarations;//指向一个函数
writefln("myFunctionPtr = ", myFunctionPtr);
//delegate可以指向类的成员函数
}
//参考dmdhtmldproperty.html
void tryProperties()
{
//每种type都有相同的一些property
writefln("float.sizeof = ", float.sizeof,
", byte.init = ",byte.init,
", int.max = ", int.max,
", uint.max = ",uint.max);
//class和struct可以有property成员
struct Foo
{
int data() { return m_data; } // read property
int data(int value) { return m_data = value; } // write property
private:
int m_data;
}
Foo f;
f.data = 3; // same as f.data(3);
f.data = f.data + 3; // same as f.data() + 3;
writefln("f.data = ",f.data);
}
2.D语言混入特性示范:
int a = 2;
mixin("int x = 2; x = 3;");
int y = 6;
x = 4;
就等价于你写:
int a = 2;
int x = 2; x = 3;
int y = 6;
x = 4;
更酷的是import语句,import(字符串差常量表达式) 语句可以把字符串常量作为文件名指定的文件内容读入并返回成为另一个字符串常量,比如:
auto str = import("foo.d");
执行结果是把 foo.d 文件的内容赋给 str。
最酷的是把两个语句联合起来使用: mixin(import("foo.d")),结果就是给 D 语言提供了#include。外加新提供的编译时字符串操纵库,使得D拥有了威力无比的meta编程能力,特别适用于代码自动化生成的场合
3.D语言中的类
import std.stdio;
import std.math;
void main()
{
tryClass();
}
class MyBaseClass
{
static float c;
static this() // static constructor用来初始化静态成员, 会在main()之前被调用
{
c = 1;
}
static ~this() // 静态析构函数在程序结束时被调用
{
writefln("program exit");
}
float x;
float y = 9; // 可以指定默认的初始值
this(float _x, float _y) // 构造函数
{
writefln("MyBaseClass.ctor");
x = _x;
y = _y;
}
~this() // Destructors
{
writefln("MyBaseClass.dtor");
}
}
class MySubClass : MyBaseClass // 只能使用单继承
{
float z;
this(float _x, float _y, float _z) // 构造函数
{
writefln("MySubClass.ctor");
super(_x, _y); //调用父类的构造函数
z = _z;
}
~this() // Destructors
{
writefln("MySubClass.dtor");
}
float getLength()
{
return sqrt(x*x + y*y + z*z);
}
/**//* invariant是D语言契约(contract)式编程的一个工具,
用来描述class固定不变的特性(characteristics);
invariant代码在类的实例构造之后、析构之前,public函数的调用时和调用完成后被调用
*/
invariant
{
assert(z*z >= 0);
writefln("-- MySubClass.invariant");
}
/**//* unittest
使用dmd的"-unittest"命令行参数将unittest代码编译进最终的可执行文件;
unittest代码在静态构造之后在main之前被自动调用
*/
unittest
{
writefln("%% MyClassSubClass.unittest");
}
}
// Scope
//----------------------------------
/**//* scope类的实例必须以scope属性来声明;
当scope类的实例离开作用域时会自动析构函数, 包括抛出异常的时候
*/
scope class MyScopeClass
{
int a;
int b;
~this()
{
writefln("go out of scope");
}
}
//----------------------------------------------
void Foo()
{
scope MyScopeClass s = new MyScopeClass;
}
void tryClass()
{
// 类的实例都是引用类型,使用new操作符创建
MyBaseClass m = new MyBaseClass(1,2);
// Field Properties
//----------------------------
m.x = 100; // field都使用“.”来引用,不再需要::和->
writefln("offset of y = ", m.y.offsetof);
// tupleof
m.tupleof[0] = 99; // m.x
m.tupleof[1] = 98; // m.y
foreach(float e;m.tupleof)
writef(e,", ");
writefln();
// 如果一个类没有指明基类,则是从Object派生的
writefln(m.toString()); // 调用Object方法
MySubClass s = new MySubClass(1,2,3);
writefln("call MySubClass.getLength()");
float length = s.getLength();
writefln("length = ", length);
assert(s); // 手动调用invariant
// Scope class
Foo();
}
4.D语言中的接口和枚举
import std.stdio;
void main()
{
tryInterface();
tryEnum();
}
// interface
//----------------------------------
interface IBase
{
void show();
}
class MyClass : IBase
{
void show()
{
writefln("MyClass");
}
}
interface IBase2
{
void foo();
}
class MyClass2 : IBase, IBase2 // 接口可以多继承
{
void show()
{
writefln(toString(),".show");
}
void foo()
{
writefln(toString(),".foo");
}
}
void tryInterface()
{
MyClass a = new MyClass;
IBase i = a; // 类对象(引用)可以转换为interface对象
a.show();
i.show();
MyClass2 b = new MyClass2;
IBase ib = b;
IBase2 i2 = b;
ib.show();
i2.foo();
}
// enum
//------------------------------------------
enum MyEnum : byte
{
A,
B,
C,
D
}
void tryEnum()
{
writefln(MyEnum.A);
writefln(MyEnum.D);
// Enum Properties
//-----------------
writefln("init = ", MyEnum.init); // 第一个元素的值
writefln("min = ", MyEnum.min);
writefln("max = ", MyEnum.max);
writefln("sizeof = ", MyEnum.sizeof);
}
5.D语言读写文件操作
import std.stream;
import std.stdio;
void main () {
File file = new File ;
File OutFile = new File;
try {
//yidabu.com试了一下,输出文件和源文件不同。可能有转义符有关
file.open("D:\\temp\\temp.html",FileMode.In);
OutFile.open("D:\\temp.html", FileMode.Out);
while(!file.eof()) {
char[] str=file.readLine();
OutFile.writeLine(str);
//printf("%.*s\n",file.readLine());
}
}
catch (Exception x) {
writefln("Datei kann nicht geoeffnet werden ");
throw x;
}
OutFile.close();
file.close();
}
6.D语言操作数据库
import dbi.sqlite.SqliteDatabase;
import std.stdio;
void main() {
SqliteDatabase db = new SqliteDatabase();
db.connect("test.db");
Row[] rows = db.queryFetchAll("SELECT * FROM names");
foreach (Row row; rows) {
writefln("name: %s\nzip: %s\n", row["name"], row["zip"]);
}
db.close();
}
通过以上我们对D语言和它的新特性有所了解,正因为这些新特性D语言在编程百花园中一枝独秀,如果看完以上内容你对D语言感兴趣,那么请你自己上网找更多的资料学习,或者经常来
http://www.easyjf.com,我们将带你一起进步。