运算符重载
C++ 的方式
假设有一个结构代表了一种新的算术类型,将其的运算符重载以使其可以和整数比较是很方便的:
struct A { int operator < (int i); int operator <= (int i); int operator > (int i); int operator >= (int i); }; int operator < (int i, A &a) { return a > i; } int operator <= (int i, A &a) { return a >= i; } int operator > (int i, A &a) { return a < i; } int operator >= (int i, A &a) { return a <= i; } |
所有的 8 个函数缺一不可。
D 的方式
D 认识到比较运算符在根本上互相之间是有联系的。所以只用一个函数是必需的:
struct A
{
int opCmp(int i);
}
编译器依照 opCmp 函数自动解释 <、<=、> 和 >= 运算符,并处理左操作数不是对象引用的情况。
类似这样的明智的规则也适用于其他的运算符重载,这就使得 D 中的运算符重载不像在 C++ 中那样繁琐且易于出错。只需要少得多的代码,就可以达到相同的效果。
名字空间 using 声明
C++ 的方式
C++ 中的 using 声明 用来从一个名字空间作用域将名字引入当前的作用域:
namespace Foo
{
int x;
}
using Foo::x;
D 的方式
D 用模块来代替名字空间和 #include 文件,用别名声明来代替 using 声明:
---- Module Foo.d ------
module Foo;
int x;
---- Another module ----
import Foo;
alias Foo.x x;
别名比简单的 using 声明灵活得多。别名可以用来重命名符号,引用模板成员,引用嵌套类型别等。
RAII(资源获得即初始化)
C++ 的方式
在 C++ 中,资源如内存等,都需要显式的处理。因为当退出当前作用域时会自动调用析构函数,RAII 可以通过将资源释放代码放进析构函数中实现:
class File
{ Handle *h;
~File()
{
h->release();
}
};
D 的方式
大多数的资源释放问题都是简单的跟踪并释放内存。在 D 中这是由垃圾收集程序自动完成的。除了内存外,用得最普遍的资源要数信号量和锁了,在 D 中可用 synchronized 声明和语句自动管理。
其余少见的情况可用 auto 类处理。Auto 类退出其作用域时,会调用它们的析构函数。
auto class File { Handle h; ~this() { h.release(); } } void test() { if (...) { auto File f = new File(); ... } // f.~this() 在反大括号处开始运行,即使是因为抛出一个异常才退出该作用域的 } |
属性
C++ 的方式
人们常常会定义一个域,同时为它提供面向对象的 get 和 set 函数:
class Abc { public: void setProperty(int newproperty) { property = newproperty; } int getProperty() { return property; } private: int property; }; Abc a; a.setProperty(3); int x = a.getProperty(); |
所有这些都不过是增加了击键的次数而已,并且还会使代码变得不易阅读,因为其中充满了 getProperty() 和 setProperty() 调用。