另见:C 程序员如何用 D 编程
Foo { Foo( x); };
Foo { ( x) { } }
A { A() {... } }; B : A { B( x) : A() // 调用基类构造函数 { ... } };
A { () { ... } } B : A { ( x) { ... (); // 调用基类构造函数 ... } }D 的方式优于 C++ 的地方在于可以灵活的在派生类的构造函数中的任何地方调用基类构造函数。D 还可以让一个构造函数调用另一个构造函数:
A { a; b; () { a = 7; b = foo(); } ( x) { (); a = x; } }也可以在调用构造函数之前初始化成员,所以上面的例子等价于:
A { a = 7; b; () { b = foo(); } ( x) { (); a = x; } }
A x, y; ... x = y;但这不适用于结构之间的比较。因此,如果要比较两个结构实例之间的等价性的话:
A x, y; ==( A& x, A& y) { (memcmp(&x, &y, ( A)) == 0); } ... (x == y) ...注意对于每个需要比较的结构来说,都要进行运算符重载,并且对运算符的重载会抛弃所有的语言提供的型别检查。C++ 的方式还有另一个问题,它不会检查 (x == y) 真正会发生什么,你不得不察看每一个被重载的 operator==() 以确定它们都做了些什么。
如果在 operator==() 中使用 memcmp() 还回造成潜在而丑陋的 bug 。由于对齐的缘故,结构的内存分布不一定是连续的,其中可能会有“洞”。C++ 并不保证这些用于对齐的洞中的值是确定的,所以两个结构实例可能拥有完全相同的结构成员,但是却因为洞中含有不同的垃圾而不相等。
为了解决这个问题,operator==() 可以实现为按成员(memberwise)比较。不幸的是,这是不可靠的,因为 (1) 如果一个成员被加入到结构定义中,程序员可能会忘记同时把它加到 operator==() 中,(2) 对于浮点数的 nan 值来说,就算它们按位比较相等,比较的结果也是不等。
在 C++ 中没有健壮的解决方案。
A x, y; ... (x == y) ...
*Handle; foo( *); bar(Handle); Handle h = HANDLE_INIT; foo(h); // 未被捕获的编码错误 bar(h); // okC++ 的解决方案是创建一个傀儡(dummy)结构,这个结构的唯一的目的就是获得真正的新型别所具有的型别检查和重载能力。
Handle { *ptr; Handle() { ptr = HANDLE_INIT; } // default initializer Handle( i) { ptr = ( *)i; } operator void*() { ptr; } // conversion to underlying type }; bar(Handle); Handle h; bar(h); h = func(); if (h != HANDLE_INIT) ...
*Handle = ( *)-1; bar(Handle); Handle h; bar(h); h = func(); if (h != Handle.init) ...注意,可以给 typedef 提供一个默认的初始值作为新型别的初始值。
A { : a; : foo(B *j); friend B; friend abc(A *); }; B { : b; : bar(A *j); friend A; }; A::foo(B *j) { j->b; } B::bar(A *j) { j->a; } abc(A *p) { p->a; }
X; A { : a; : foo(B j) { j.b; } } B { : b; : bar(A j) { j.a; } } abc(A p) { p.a; }private 特征禁止从其他模块中访问成员。
A { < ( i); <= ( i); > ( i); >= ( i); }; < ( i, A &a) { a > i; } <= ( i, A &a) { a >= i; } > ( i, A &a) { a < i; } >= ( i, A &a) { a <= i; }所有的 8 个函数缺一不可。
A { opCmp( i); }编译器依照 opCmp 函数自动解释 <、<=、> 和 >= 运算符,并处理左操作数不是对象引用的情况。
类似这样的明智的规则也适用于其他的运算符重载,这就使得 D 中的运算符重载不像在 C++ 中那样繁琐且易于出错。只需要少得多的代码,就可以达到相同的效果。
Foo { x; } Foo::x;
---- Module Foo.d ------ Foo; x; ---- Another module ---- Foo; Foo.x x;别名比简单的 using 声明灵活得多。别名可以用来重命名符号,引用模板成员,引用嵌套类型别等。
File { Handle *h; ~File() { h->release(); } };
其余少见的情况可用 auto 类处理。Auto 类退出其作用域时,会调用它们的析构函数。
File { Handle h; () { h.release(); } } test() { (...) { File f = File(); ... } }
Abc { : setProperty( newproperty) { property = newproperty; } getProperty() { property; } : property; }; Abc a; a.setProperty(3); x = a.getProperty();所有这些都不过是增加了击键的次数而已,并且还会使代码变得不易阅读,因为其中充满了 getProperty() 和 setProperty() 调用。
Abc { property( newproperty) { myprop = newproperty; } property() { myprop; } : myprop; }使用时:
Abc a; a.property = 3; x = a.property;因此,在 D 中属性可以被看作一个简单的域名。开始时,属性可以只是一个简单的域名,但是如果后来需要将读取和设置行为改变为函数调用,只需要改动类的定义就够了。这样就避免了定义 get 和 set 时敲入冗长的代码,仅仅是为了‘谨防’日后派生类有可能会不得不重载它们。这也是一种定义接口类的方法,这些类没有数据域,只在语法上表现得好像它们作了实际工作。
< n> factorial { : { result = n * factorial::result }; }; <> factorial<1> { : { result = 1 }; }; test() { printf("%d/n", factorial<4>::result); }
factorial( n) { { factorial = n* .factorial!(n-1) } } factorial( n : 1) { { factorial = 1 } } test() { printf("%d/n", factorial!(4)); }