.. _structs: ====== 结构 ====== .. index:: single: Structs Daslang 使用类似于 C/C++、Java、C# 等语言的结构机制。 但是,存在一些重要的区别。 结构体是一等对象,如整数或字符串,可以存储在表槽、其他结构体、局部变量、数组、元组、变体等中,并作为函数参数传递。 ------------------ 结构体声明 ------------------ .. index:: pair: declaration; Struct single: Struct Declaration 通过关键字 ``struct`` 创建结构体对象:: struct Foo { x, y: int xf: float } 结构可以是 ``private`` 或 ``public``:: struct private Foo { x, y: int } struct public Bar { xf: float } 如果未指定,则结构继承模块 public(即在公共模块中,结构是 public,而在私有模块中,结构是私有的)。 结构实例是通过 'new expression' 或变量声明语句创建的:: let foo: Foo let foo: Foo? = new Foo() 有意没有成员函数。只有数据成员,因为它本身就是一种数据类型。 结构可以处理将函数类型作为数据的成员(这意味着它是一个可以在执行期间更改的函数指针)。 有一些初始值设定项可以简化编写复杂结构初始化的过程。 基本上,与结构体本身同名的函数用作初始化器。 如果有任何成员具有初始值设定项,则编译器将生成 'default' 初始值设定项:: struct Foo { x: int = 1 y: int = 2 } 默认情况下,结构字段初始化为零,无论成员的“初始值设定项”如何,除非你专门调用初始值设定项:: let fZero : Foo // no initializer is called, x, y = 0 let fInited = Foo() // initializer is called, x = 1, y = 2 在可能的情况下,推断结构体字段类型:: struct Foo { x = 1 // inferred as int y = 2.0 // inferred as float } 创建期间的显式结构初始化会将所有未初始化的成员归零:: let fExplicit = Foo(uninitialized x=13) // x = 13, y = 0 前面的代码示例是:: let fExplicit: Foo fExplicit.x = 13 构造后初始化只需要指定覆盖的字段:: let fPostConstruction = Foo(x=13) // x = 13, y = 2 前面的代码示例是:: let fPostConstruction: Foo fPostConstruction.x = 13 fPostConstruction.y = 2 “Clone initializer” 是一种有用的模式,当两个结构都在堆上时,用于创建现有结构的克隆:: def Foo ( p : Foo? ) { // “clone initializer” 采用指向现有结构的指针 var self := *p return <- self } ... let a = new Foo(x=1, y=2.) // 在堆上创建新的 Foo 实例,初始化它 let b = new Foo(a) // 在此处创建 b 的克隆 -------------------------- 结构函数成员 -------------------------- Daslang 没有嵌入的结构成员函数、虚拟 (可以在继承的结构中重写) 或非虚拟。 这些功能是为类实现的。 为了便于面向对象编程,非虚杆件函数可以用管道作符 ``|>`` 轻松模拟:: struct Foo { x, y: int = 0 } def setXY(var self: Foo; X, Y: int) { with ( self ) { x = X y = Y } } var foo: Foo foo |> setXY(10, 11) // 这是 setXY(foo, 10, 11) 的语法糖 setXY(foo, 10, 11) // 与上面的行完全相同 由于函数指针是一个东西,因此可以通过将函数指针存储为成员来模拟“虚拟”函数:: struct Foo { x, y: int = 0 set = @@setXY } def setXY(var self: Foo; X, Y: int) { with ( self ) { x = X y = Y } } ... var foo: Foo = Foo() foo->set(1, 2) // 如果在派生类中重写,则可以调用其他内容。 // 它也只是函数指针调用的语法糖 invoke(foo.set, foo, 1, 2) // 与上述完全相同 这使得 OOP 范例中虚拟调用和非虚拟调用之间的区别变得明确。 事实上,Daslang 类正是以这种方式实现虚拟函数的。 可以在结构体中声明虚函数。这相当于上面的示例:: struct Foo { x, y: int = 0 def setXY(X, Y: int) { x = X y = Y } } ----------- 继承 ----------- .. index:: pair: inheritance; Struct single: Inheritance Daslang 的结构支持单继承,方法是在结构声明中添加 ' : ',后跟父结构的名称。 派生结构的语法如下:: struct Bar: Foo { yf: float } 声明派生结构后,Daslang 首先将所有 base 的成员复制到新结构中,然后继续计算声明的其余部分。 派生结构具有其基本结构的所有成员。它只是先手动复制所有成员的语法糖。 可以在派生结构中覆盖虚函数:: struct Foo { x, y: int = 0 def setXY(X, Y: int) { x = X y = Y } } struct Bar: Foo { yf: float = 0.0 def override setXY(X, Y: int) { x = X + 1 y = Y + 1 yf = x + y } } .. _structs_alignment: --------- 对齐 --------- 结构体大小和对齐方式类似于 C++: * 单个成员单独对齐 * 整体结构对齐是最大成员对齐的对齐 继承的结构对齐可以通过 [cpp_layout] 注释进行控制:: [cpp_layout (pod=false)] struct CppS1 { vtable : void? // 我们正在模拟 C++ 类 b : int64 = 2l c : int = 3 } [cpp_layout (pod=false)] struct CppS2 : CppS1 { // d 将在类边界上对齐 d : int = 4 } -------------------------- OOP 实现详细信息 -------------------------- 在结构之上,有足够的基础设施来支持基本的 OOP。 但是,它已经以类的形式提供,具有一些固定的内存开销 (参阅 :ref:`Classes `). 可以使用 override 语法覆盖基类的方法。 下面是一个示例: :: struct Foo { x, y: int = 0 set = @@Foo_setXY } def Foo_setXY(var this: Foo; x, y: int) { this.x = x this.y = y } struct Foo3D: Foo { z: int = 3 override set = cast @@Foo3D_setXY } def Foo3D_setXY(var thisFoo: Foo3D; x, y: int) { thisFoo.x = x thisFoo.y = y thisFoo.z = -1 } 使用 ``cast`` 关键字将派生结构实例强制转换为其父类型是安全的:: var f3d: Foo3D = Foo3D() (cast f3d).y = 5 将 base 结构体强制转换为其派生的子类型是不安全的:: var f3d: Foo3D = Foo3D() def foo(var foo: Foo) { (cast foo).z = 5 // error, won't compile } 如果需要,upcast 可以与 ``unsafe`` 关键字一起使用:: struct Foo { x: int } struct Foo2:Foo { y: int } def setY(var foo: Foo; y: int) { // 警告!如果它不是真正的 Foo2,可能会对你的应用程序造成糟糕的影响 unsafe { (upcast foo).y = y } } 由于上面的示例非常危险,为了使其更安全,您可以将其修改为以下内容:: struct Foo { x: int typeTag: uint = hash("Foo") } struct Foo2:Foo { y: int override typeTag: uint = hash("Foo2") } def setY(var foo: Foo; y: int) { // 这不会做任何真正坏的事情,但会在错误的引用时 panic unsafe { if ( foo.typeTag == hash("Foo2") ) { (upcast foo).y = y print("Foo2 type references was passed\n") } else { assert(false, "Not Foo2 type references was passed\n") } } }