知识若不分享 实在没有意义 http://www.d-programming-language-china.org 20070423
点击下面网址查看原文:
http://www.d-programming-language-china.org
by: uFramer D语言论坛 http://www.d-programming-language-china.org
from: http://www.digitalmars.com/d/class.html
version: 基于D 1.013 (Apr 19, 2007)
D的面向对象的特性都来源于类。处在类层次最顶点的是类 Object。Object 定义了每个派生类拥有的最小功能集合,并为这些功能提供了默认的实现。
类是程序员定义的类型。作为面向对象语言,D支持继承和多态。D 类支持单继承编程范式,并且支持接口。类对象只能通过引用实例化。
类可以被导出,这意味着它的名字和非私有成员将会暴露给外部的 DLL 或者 EXE 。
类声明定义为:
ClassDeclaration:
class Identifier BaseClassListopt ClassBody
BaseClassList:
: SuperClass
: SuperClass InterfaceClasses
: InterfaceClass
SuperClass:
Identifier
Protection Identifier
InterfaceClasses:
InterfaceClass
InterfaceClass InterfaceClasses
InterfaceClass:
Identifier
Protection Identifier
Protection:
private
package
public
export
ClassBody:
{ }
{ ClassBodyDeclarations }
ClassBodyDeclarations:
ClassBodyDeclaration
ClassBodyDeclaration ClassBodyDeclarations
ClassBodyDeclaration:
Declaration
Constructor
Destructor
StaticConstructor
StaticDestructor
Invariant
UnitTest
ClassAllocator
ClassDeallocator
类的组成:
父类
接口
动态域
静态域
类型
函数
静态函数
动态函数
构造函数
析构函数
静态构造函数
静态析构函数
不变量
单元测试
分配器
释放器
类定义:
注意在标志类定义结束的‘ }’之后没有‘;’。同样不能像下面这样声明变量:( 本文出处: http://www.d-programming-language-china.org )
应该这样声明:
Fields域
类成员总是通过‘.’运算符访问。没有 C++ 中的‘::’或 ‘->’运算符。
D 编译器有权利重新排列类中各个域的顺序,这样就允许编译器按照实现定义的方式将它们压缩以优化程序。因此,对齐语句,匿名结构以及匿名联合不允许在类中出现,因为它们依赖于数据的排列方式。考虑函数中的局部变量的域——编译器将其中一些分配到寄存器中,其他的按照最理想的分布被保存到堆栈帧中。这就给了代码设计者重排代码以加强可读性的自由,而不必强迫代码设计者 依据机器的优化规则排列相关的域。结构/联合提供了显式控制域分布的能力,但这不是类的分内之事。
Field Properties域属性
The .offsetof property gives the offset in bytes of the field from the beginning of the class instantiation. .offsetof can only be applied to fields qualified with the type of the class, not expressions which produce the type of the field itself:
class Foo
{
int x;
}
...
void test(Foo foo)
{
size_t o;
o = Foo.x.offsetof; // yields 8
o = foo.x.offsetof; // error, .offsetof an int type
}
Class Properties
The .tupleof property returns an ExpressionTuple of all the fields in the class, excluding the hidden fields and the fields in the base class.( 本文出处: http://www.d-programming-language-china.org )
class Foo { int x; long y; }
void test(Foo foo)
{
foo.tupleof[0] = 1; // set foo.x to 1
foo.tupleof[1] = 2; // set foo.y to 2
foreach (x; foo.tupleof)
writef(x); // prints 12
}
Super Class父类
所有的类都从父类继承。如果没有指定,它就继承 Object 。Object 是 D 类层次体系的根。
Constructors构造函数
Constructor:
this Parameters FunctionBody
所有的成员都被初始化为它对应类型的默认初始值,通常整数被初始化为 0 ,浮点数被初始化为 NAN。这就防止了因为在某个构造函数中忽略了初始化某个成员而造成那些隐藏的错误。在类定义中,可以使用静态的初始值代替默认值:
class Abc
{
int a; // a 的默认初始值为 0
long b = 7; // b 的默认初始值为 7
float f; // f 的默认初始值为 NAN
}
静态初始化在调用构造函数之前完成。
构造函数是名为 this 的函数,它没有返回值:
class Foo
{
this(int x) // 声明 Foo 的构造函数
{ ...
}
this()
{ ...
}
}
基类的构造通过用 super 调用基类的构造函数来完成:
class A { this(int y) { } }
class B : A
{
int j;
this()
{
...
super(3); // 调用基类构造函数 A.this(3)
...
}
}
构造函数也能调用同一个类中的其他构造函数以共享通用的初始化代码:( 本文出处: http://www.d-programming-language-china.org )
class C
{
int j;
this()
{
...
}
this(int i)
{
this();
j = i;
}
如果构造函数中没有通过 this 或 super 调用构造函数,并且基类有构造函数,编译器将在构造函数的开始处自动插入一个 super() 。
如果类没有构造函数,但是基类有构造函数,那么默认的构造函数的形式为:
这会由编译器隐式生成。
类对象的构造十分灵活,但是有一些限制:
构造函数互相调用是非法的:
this() { this(1); }
this(int i) { this(); } // 非法,构造函数循环调用
如果一个构造函数中调用了构造函数,那么这个构造函数的任何执行路径中都只能调用一次构造函数:
this() { a || super(); } // illegal
this() { (a) ? this(1) : super(); } // ok
this()
{
for (...)
{
super(); // illegal, inside loop
}
}
在构造函数出现之前显式或者隐式引用 this 都是非法的。
不能在标号后调用构造函数(这样做的目的是使检查 goto 的前导条件容易完成)。
类对象的实例使用 New表达式 创建:( 本文出处: http://www.d-programming-language-china.org )
在这个过程中按照下面的步骤执行:
为对象分配存储空间。如果失败,不会返回 null,会抛出一个 OutOfMemoryException 异常。因此,不再需要编写冗长而乏味的 null 引用防卫代码。
使用类定义中的值静态初始化“原始数据”。给指向vtbl[] (指向虚函数的指针数组)的指针赋值。这保证了调用构造函数的类是已经完全成型的。这个操作等价于使用 内存复制 将对象的静态版本拷贝到新分配的对象的空间,但是更高级的编译器将会对这种方法进行优化。
如果为类定义了构造函数,匹配调用参数列表的构造函数被调用。
如果打开了类不变量的检查,在构造函数调用后调用类不变量。
Destructors析构函数
Destructor:
~this() FunctionBody
当对象被垃圾收集程序删除时将调用析构函数。语法如下:
class Foo
{
~this() // Foo 的析构函数
{
}
}
每个类只能有一个析构函数,析构函数没有参数,没有特征。它总是虚函数。
此构函数的作用是释放对象持有的任何资源。
程序可以显式的通知垃圾收集程序不再引用一个对象(使用 delete 表达式),然后垃圾收集程序会立即调用析构函数,并将对象占用的内存放回自由存储区。析构函数决不会被调用两次。( 本文出处: http://www.d-programming-language-china.org )
当析构函数运行结束时会自动调用父类的析构函数。不能显式调用父类的析构函数。
当垃圾收集程序调用一个对象的析构函数时,并且这个对象有成员引用到已被垃圾收集的对象,那么这些引用都会变得无效。这意味着析构函数不能引用子对象。这条规则并不适用于 auto 对象或使用 释放表达式 释放的对象。
垃圾收集程序不能保证对所有的无引用对象调用析构函数。而且垃圾收集程序也不能保证调用的相对顺序。
从数据段中引用的对象不会被垃圾收集程序收集。
Static Constructors静态构造函数
StaticConstructor:
static this() FunctionBody
静态构造函数在 main() 函数获得控制前执行初始化。静态构造函数用来初始化其值不能在编译时求出的静态类成员。
其他语言中的静态构造函数被设计为可以使用成员初始化隐式构造,这样做的问题是无法编译时计算精确控制代码执行的顺序。例如:( 本文出处: http://www.d-programming-language-china.org )
class Foo
{
static int a = b + 1;
static int b = a * 2;
}
最终 a 和 b 都是什么值?初始化按照什么顺序执行?在初始化前 a 和 b 都是什么值?这是一个编译是错误吗?抑或它是一个运行时错误?还有一种不那么明显的令人迷惑的情况是单独一个初始化是静态的还是动态的。
D 让这一切变得简单。所有的成员初始化都必须在编译时可确定,因此就没有求值顺序依赖问题,也不可能读取一个未被初始化的值。动态初始化由静态构造函数执行,采用语法 static this() 实现。
class Foo
{
static int a; // 默认初始化为 0
static int b = 1;
static int c = b + a; // 错误,不是常量初始化
static this() // 静态构造函数
{
a = b + 1; // a 被设为 2
b = a * 2; // b 被设为 4
}
}
static this() 会由启动代码在调用 main() 之前调用。如果它正常返回(没有抛出异常),静态析构函数就会被加到会在程序终止时被调用的函数列表中。静态构造函数的参数列表为空。
模块内的静态构造函数按出现的词法顺序执行。
All the static constructors for modules that are directly or indirectly imported are executed before the static constructors for the importer.
静态构造函数声明里的static不是特征,必须直接出现在this前面:( 本文出处: http://www.d-programming-language-china.org )
class Foo
{
static this() { ... } // a static constructor
static private this() { ... } // not a static constructor
static
{
this() { ... } // not a static constructor
}
static:
this() { ... } // not a static constructor
}
Static Destructor静态析构函数
StaticDestructor:
static ~this() FunctionBody
静态析构函数定义为具有语法形式 static ~this() 的特殊静态函数。( 本文出处: http://www.d-programming-language-china.org )
class Foo
{
static ~this() // 静态析构函数
{
}
}
静态析构函数在程序终止的时候被调用,但这只发生在静态构造函数成功执行完成时。静态析构函数的参数列表为空。静态析构函数按照静态构造函数调用的逆顺序调用。
静态析构函数声明里的static不是特征,必须直接出现在~this前面:
class Foo
{
static ~this() { ... } // a static destructor
static private ~this() { ... } // not a static destructor
static
{
~this() { ... } // not a static destructor
}
static:
~this() { ... } // not a static destructor
}
Class Invariants类不变量
类不变量:
invariant 语句块
ClassInvariant:
invariant BlockStatement
类不变量用来指定类的必须总是为真的特征(除了在执行成员函数时)。例如,代表日期的类可能有一个不变量—— day 必须位于 1..31 之间,hour 必须位于 0..23 之间:
class Date
{
int day;
int hour;
invariant
{
assert(1 <= day && day <= 31);
assert(0 <= hour && hour < 24);
}
}
类不变量就是一种契约,是必须为真的断言。当类的构造函数执行完成时、在类的析构函数开始执行时、在 public 或 exported 成员函数执行之前,或者在 public 或 exported 成员函数完成时,将检查不变量。
不变量中的代码不应该调用任何非静态公有类成员函数,无论直接还是间接。如果那样做的话就会造成堆栈溢出,因为不变量将被无限递归调用。( 本文出处: http://www.d-programming-language-china.org )
class Foo
{
public void f() { }
private void g() { }
invariant
{
f(); // 错误,不能在不变量中调用公有成员函数
g(); // ok,g() 不是公有的
}
}
可以使用 assert() 检查类对象的不变量,例如:
Date mydate;
...
assert(mydate); // 检查类 Date 的不变量
不变量包含断言表达式,如果不变量检查失败,将抛出一个 AssertErrors 异常。 类不变量会被继承,也就是,任何类的不变量都隐式地包含其基类的不变量。
每个类只能有一个 类不变量 。
当编译生成发布版时,不会生成不变量检查代码,这样程序就会以最高速度运行。
Unit Tests单元测试
UnitTest:
unittest FunctionBody
单元测试是用来测试一个类是否工作正常的一系列测试用例。理想情况下,单元测试应该在每次编译时运行一遍。确保做到这点的最好的方法是将它们同类的代码一同放到类的实现代码中,一起维护。
D 的类可以有一个特殊的成员函数,叫做:
一个编译器开关,如DMD用-unittest,使单元测试代码编译并整合到可执行文件中。程序中所有类的 test() 函数将在静态初始化之后,maiin()函数调用之前被调用。
例如,假定一个类 Sum 用来计算两个值得和:( 本文出处: http://www.d-programming-language-china.org )
class Sum
{
int add(int x, int y) { return x + y; }
unittest
{
Sum sum = new Sum;
assert(sum.add(3,4) == 7);
assert(sum.add(-2,0) == -2);
}
}
Class Allocators类分配器
ClassAllocator:
new Parameters FunctionBody
具有下面形式的类成员函数叫做类分配器:
类分配器可以有任意数量的参数,比如第一个参数的类型是 uint。可以为类定义多个类分配器,通过通用的函数重载解析规则选择合适的函数。当执行一个 new 表达式:
并且当 Foo 是拥有分配器的类时,分配器将被调用,第一个参数被设置为分配一个实例所需的以字节为单位的内存大小。分配器必须分配内存并返回一个 void* 指针。如果分配失败,它不必返回一个 null,但是必须抛出一个异常。如果分配器有多于一个的参数,余下的参数将在 NewExpression 之中的 new 之后的括号中出现:
class Foo
{
this(char[] a) { ... }
new(uint size, int x, int y)
{
...
}
}
...
new(1,2) Foo(a); // 调用 new(Foo.size,1,2)
如果没有指定类分配器,派生类将继承基类的类分配器。
The class allocator is not called if the instance is created on the stack.
另见 显式类实例分配 。
Class Deallocators类释放器
ClassDeallocator:
delete Parameters FunctionBody
具有下面形式的类成员函数叫做类释放器:( 本文出处: http://www.d-programming-language-china.org )
类释放器有且仅有一个类型为 void* 的参数。一个类只能有一个类释放器。当执行一个 delete 表达式:
且 f 是拥有释放器的一个类的实例时,如果类有析构函数,会调用析构函数,然后释放器被调用,一个指向类实例的指针被传递给释放器。释放内存是释放器的责任。
如果不特别指定,派生类会继承基类所有的释放器。
The class allocator is not called if the instance is created on the stack.
参见 显式类实例分配 。
Scope Classes
Scope类是带有 scope 特征的类,如下:
scope特征会被继承,所以任何从 scope 类继承的类都是 scope 的。
对 scope 类的引用只能作为函数局部变量出现,且这个引用必须被声明为 scope :( 本文出处: http://www.d-programming-language-china.org )
scope class Foo { ... }
void func()
{
Foo f; // error, reference to scope class must be scope
scope Foo g = new Foo(); // correct
}
当一个 scope 类的引用脱离作用域时,会自动调用它的析构函数(如果有的话)。即使是因为发生了异常而脱离作用域,也会保证这种行为发生。
Final Classes
Final classes cannot be subclassed:
final class A { }
class B : A { } // error, class A is final
Nested Classes
A nested class is a class that is declared inside the scope of a function or another class. A nested class has access to the variables and other symbols of the classes and functions it is nested inside:
class Outer
{
int m;
class Inner
{
int foo()
{
return m; // Ok to access member of Outer
}
}
}
void func()
{ int m;
class Inner
{
int foo()
{
return m; // Ok to access local variable m of func()
}
}
}
If a nested class has the static attribute, then it can not access variables of the enclosing scope that are local to the stack or need a this:
class Outer
{
int m;
static int n;
static class Inner
{
int foo()
{
return m; // Error, Inner is static and m needs a this
return n; // Ok, n is static
}
}
}
void func()
{ int m;
static int n;
static class Inner
{
int foo()
{
return m; // Error, Inner is static and m is local to the stack
return n; // Ok, n is static
}
}
}
Non-static nested classes work by containing an extra hidden member (called the context pointer) that is the frame pointer of the enclosing function if it is nested inside a function, or the this of the enclosing class's instance if it is nested inside a class.
When a non-static nested class is instantiated, the context pointer is assigned before the class's constructor is called, therefore the constructor has full access to the enclosing variables. A non-static nested class can only be instantiated when the necessary context pointer information is available:( 本文出处: http://www.d-programming-language-china.org )
class Outer
{
class Inner { }
static class SInner { }
}
void func()
{
class Nested { }
Outer o = new Outer; // Ok
Outer.Inner oi = new Outer.Inner; // Error, no 'this' for Outer
Outer.SInner os = new Outer.SInner; // Ok
Nested n = new Nested; // Ok
}
While a non-static nested class can access the stack variables of its enclosing function, that access becomes invalid once the enclosing function exits:
class Base
{
int foo() { return 1; }
}
Base func()
{ int m = 3;
class Nested : Base
{
int foo() { return m; }
}
Base b = new Nested;
assert(b.foo() == 3); // Ok, func() is still active
return b;
}
int test()
{
Base b = func();
return b.foo(); // Error, func().m is undefined
}
If this kind of functionality is needed, the way to make it work is to make copies of the needed variables within the nested class's constructor:
class Base
{
int foo() { return 1; }
}
Base func()
{ int m = 3;
class Nested : Base
{ int m_;
this() { m_ = m; }
int foo() { return m_; }
}
Base b = new Nested;
assert(b.foo() == 3); // Ok, func() is still active
return b;
}
int test()
{
Base b = func();
return b.foo(); // Ok, using cached copy of func().m
}
A this can be supplied to the creation of an inner class instance by prefixing it to the NewExpression:
class Outer
{ int a;
class Inner
{
int foo()
{
return a;
}
}
}
int bar()
{
Outer o = new Outer;
o.a = 3;
Outer.Inner oi = o.new Inner;
return oi.foo(); // returns 3
}
Here o supplies the this to the outer class instance of Outer.
The property .outer used in a nested class gives the this pointer to its enclosing class. If the enclosing context is not a class, the .outer will give the pointer to it as a void* type.( 本文出处: http://www.d-programming-language-china.org )
class Outer
{
class Inner
{
Outer foo()
{
return this.outer;
}
}
void bar()
{
Inner i = new Inner;
assert(this == i.foo());
}
}
void test()
{
Outer o = new Outer;
o.bar();
}
Anonymous Nested Classes
An anonymous nested class is both defined and instantiated with a NewAnonClassExpression:
NewAnonClassExpression:
new (ArgumentList)opt class (ArgumentList)opt SuperClassopt InterfaceClassesopt ClassBody
which is equivalent to:
class Identifier : SuperClass InterfaceClasses
ClassBody
new (ArgumentList) Identifier (ArgumentList);
where Identifier is the name generated for the anonymous nested class.
( lastupdate:20070426 最新文章请访问http://www.d-programming-language-china.org )关于一大步成功社区:
yidabu提倡在交流中学习,在分享中提高
收集感兴趣的知识,写下心得,通过网络与别人一起分享
理解一点就实践一步,收获什么就分享什么,成功就是这样一点点一步步累积起来的
网络只是一个工具,只有自己身心提高才是实实在在的。d-programming-language-china.org为大家提供一个学习交流各种知识的平台