mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-12 00:11:36 +08:00
@@ -12,9 +12,9 @@ type identifier struct {
|
||||
|
||||
`type T struct {a, b int}` 也是合法的语法,它更适用于简单的结构体。
|
||||
|
||||
结构体里的字段都有 **名字**,像 field1、field2 等,如果字段在代码中从来也不会被用到,那么可以命名它为 **_**。
|
||||
结构体里的字段都有 **名字**,像 `field1`、`field2` 等,如果字段在代码中从来也不会被用到,那么可以命名它为 `_`。
|
||||
|
||||
结构体的字段可以是任何类型,甚至是结构体本身(参考第 [10.5](10.5.md) 节),也可以是函数或者接口(参考第 11 章)。可以声明结构体类型的一个变量,然后像下面这样给它的字段赋值:
|
||||
结构体的字段可以是任何类型,甚至是结构体本身(参考第 [10.5](10.5.md) 节),也可以是函数或者接口(参考[第 11 章](11.0.md))。可以声明结构体类型的一个变量,然后像下面这样给它的字段赋值:
|
||||
|
||||
```go
|
||||
var s T
|
||||
@@ -24,18 +24,18 @@ s.b = 8
|
||||
|
||||
数组可以看作是一种结构体类型,不过它使用下标而不是具名的字段。
|
||||
|
||||
**使用 new**
|
||||
**使用 `new()`**
|
||||
|
||||
使用 **new** 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:`var t *T = new(T)`,如果需要可以把这条语句放在不同的行(比如定义是包范围的,但是分配却没有必要在开始就做)。
|
||||
使用 `new()` 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:`var t *T = new(T)`,如果需要可以把这条语句放在不同的行(比如定义是包范围的,但是分配却没有必要在开始就做)。
|
||||
|
||||
```go
|
||||
var t *T
|
||||
t = new(T)
|
||||
```
|
||||
|
||||
写这条语句的惯用方法是:`t := new(T)`,变量 `t` 是一个指向 `T`的指针,此时结构体字段的值是它们所属类型的零值。
|
||||
写这条语句的惯用方法是:`t := new(T)`,变量 `t` 是一个指向 `T` 的指针,此时结构体字段的值是它们所属类型的零值。
|
||||
|
||||
声明 `var t T` 也会给 `t` 分配内存,并零值化内存,但是这个时候 `t` 是类型 T 。在这两种方式中,`t` 通常被称做类型 T 的一个实例(instance)或对象(object)。
|
||||
声明 `var t T` 也会给 `t` 分配内存,并零值化内存,但是这个时候 `t` 是类型 `T` 。在这两种方式中,`t` 通常被称做类型 T 的一个实例 (instance) 或对象 (object)。
|
||||
|
||||
示例 10.1 [structs_fields.go](examples/chapter_10/structs_fields.go) 给出了一个非常简单的例子:
|
||||
|
||||
@@ -69,13 +69,13 @@ func main() {
|
||||
The string is: Chris
|
||||
&{10 15.5 Chris}
|
||||
|
||||
使用 `fmt.Println` 打印一个结构体的默认输出可以很好的显示它的内容,类似使用 **%v** 选项。
|
||||
使用 `fmt.Println()` 打印一个结构体的默认输出可以很好的显示它的内容,类似使用 `%v` 选项。
|
||||
|
||||
就像在面向对象语言所作的那样,可以使用点号符给字段赋值:`structname.fieldname = value`。
|
||||
|
||||
同样的,使用点号符可以获取结构体字段的值:`structname.fieldname`。
|
||||
|
||||
在 Go 语言中这叫 **选择器(selector)**。无论变量是一个结构体类型还是一个结构体类型指针,都使用同样的 **选择器符(selector-notation)** 来引用结构体的字段:
|
||||
在 Go 语言中这叫 **选择器 (selector)**。无论变量是一个结构体类型还是一个结构体类型指针,都使用同样的 **选择器符 (selector-notation)** 来引用结构体的字段:
|
||||
|
||||
```go
|
||||
type myStruct struct { i int }
|
||||
@@ -99,7 +99,7 @@ p.i
|
||||
ms = struct1{10, 15.5, "Chris"}
|
||||
```
|
||||
|
||||
混合字面量语法(composite literal syntax)`&struct1{a, b, c}` 是一种简写,底层仍然会调用 `new ()`,这里值的顺序必须按照字段顺序来写。在下面的例子中能看到可以通过在值的前面放上字段名来初始化字段的方式。表达式 `new(Type)` 和 `&Type{}` 是等价的。
|
||||
混合字面量语法 (composite literal syntax) `&struct1{a, b, c}` 是一种简写,底层仍然会调用 `new()`,这里值的顺序必须按照字段顺序来写。在下面的例子中能看到可以通过在值的前面放上字段名来初始化字段的方式。表达式 `new(Type)` 和 `&Type{}` 是等价的。
|
||||
|
||||
时间间隔(开始和结束时间以秒为单位)是使用结构体的一个典型例子:
|
||||
|
||||
@@ -118,7 +118,7 @@ intr := Interval{end:5, start:1} (B)
|
||||
intr := Interval{end:5} (C)
|
||||
```
|
||||
|
||||
在(A)中,值必须以字段在结构体定义时的顺序给出,**&** 不是必须的。(B)显示了另一种方式,字段名加一个冒号放在值的前面,这种情况下值的顺序不必一致,并且某些字段还可以被忽略掉,就像(C)中那样。
|
||||
在 (A) 中,值必须以字段在结构体定义时的顺序给出,`&` 不是必须的。(B) 显示了另一种方式,字段名加一个冒号放在值的前面,这种情况下值的顺序不必一致,并且某些字段还可以被忽略掉,就像 (C) 中那样。
|
||||
|
||||
结构体类型和字段的命名遵循可见性规则(第 [4.2](04.2.md) 节),一个导出的结构体类型中有些字段是导出的,另一些不是,这是可能的。
|
||||
|
||||
@@ -128,7 +128,7 @@ intr := Interval{end:5} (C)
|
||||
type Point struct { x, y int }
|
||||
```
|
||||
|
||||
使用 new 初始化:
|
||||
使用 `new()` 初始化:
|
||||
|
||||

|
||||
|
||||
@@ -136,9 +136,9 @@ type Point struct { x, y int }
|
||||
|
||||

|
||||
|
||||
类型 struct1 在定义它的包 pack1 中必须是唯一的,它的完全类型名是:`pack1.struct1`。
|
||||
类型 `struct1` 在定义它的包 `pack1` 中必须是唯一的,它的完全类型名是:`pack1.struct1`。
|
||||
|
||||
下面的例子 [Listing 10.2—person.go](examples/chapter_10/person.go) 显示了一个结构体 Person,一个方法,方法有一个类型为 `*Person` 的参数(因此对象本身是可以被改变的),以及三种调用这个方法的不同方式:
|
||||
下面的例子 [Listing 10.2—person.go](examples/chapter_10/person.go) 显示了一个结构体 `Person`,一个方法 `upPerson()`,方法有一个类型为 `*Person` 的参数(因此对象本身是可以被改变的),以及三种调用这个方法的不同方式:
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -209,7 +209,7 @@ type Rect2 struct {Min, Max *Point }
|
||||
|
||||

|
||||
|
||||
这块的 `data` 字段用于存放有效数据(比如 float64),`su` 指针指向后继节点。
|
||||
这块的 `data` 字段用于存放有效数据(比如 `float64`),`su` 指针指向后继节点。
|
||||
|
||||
Go 代码:
|
||||
|
||||
@@ -220,7 +220,7 @@ type Node struct {
|
||||
}
|
||||
```
|
||||
|
||||
链表中的第一个元素叫 `head`,它指向第二个元素;最后一个元素叫 `tail`,它没有后继元素,所以它的 `su` 为 nil 值。当然真实的链接会有很多数据节点,并且链表可以动态增长或收缩。
|
||||
链表中的第一个元素叫 `head`,它指向第二个元素;最后一个元素叫 `tail`,它没有后继元素,所以它的 `su` 为 `nil` 值。当然真实的链接会有很多数据节点,并且链表可以动态增长或收缩。
|
||||
|
||||
同样地可以定义一个双向链表,它有一个前趋节点 `pr` 和一个后继节点 `su`:
|
||||
|
||||
@@ -234,9 +234,9 @@ type Node struct {
|
||||
|
||||
二叉树:
|
||||
|
||||

|
||||
<img src="images/10.1_fig10.4.jpg?raw=true" style="zoom: 80%;" />
|
||||
|
||||
二叉树中每个节点最多能链接至两个节点:左节点(le)和右节点(ri),这两个节点本身又可以有左右节点,依次类推。树的顶层节点叫根节点(**root**),底层没有子节点的节点叫叶子节点(**leaves**),叶子节点的 `le` 和 `ri` 指针为 nil 值。在 Go 中可以如下定义二叉树:
|
||||
二叉树中每个节点最多能链接至两个节点:左节点 (`le`) 和右节点 (`ri`),这两个节点本身又可以有左右节点,依次类推。树的顶层节点叫根节点 (**root**),底层没有子节点的节点叫叶子节点 (**leaves**),叶子节点的 `le` 和 `ri` 指针为 `nil` 值。在 Go 中可以如下定义二叉树:
|
||||
|
||||
```go
|
||||
type Tree struct {
|
||||
@@ -248,7 +248,7 @@ type Tree struct {
|
||||
|
||||
**结构体转换**
|
||||
|
||||
Go 中的类型转换遵循严格的规则。当为结构体定义了一个 alias 类型时,此结构体类型和它的 alias 类型都有相同的底层类型,它们可以如示例 10.3 那样互相转换,同时需要注意其中非法赋值或转换引起的编译错误。
|
||||
Go 中的类型转换遵循严格的规则。当为结构体定义了一个 `alias` 类型时,此结构体类型和它的 `alias` 类型都有相同的底层类型,它们可以如示例 10.3 那样互相转换,同时需要注意其中非法赋值或转换引起的编译错误。
|
||||
|
||||
示例 10.3:
|
||||
|
||||
@@ -278,30 +278,29 @@ func main() {
|
||||
|
||||
{5} {5} {5}
|
||||
|
||||
**练习 10.1** vcard.go:
|
||||
**练习 10.1** [vcard.go](exercises\chapter_10\vcard.go):
|
||||
|
||||
定义结构体 Address 和 VCard,后者包含一个人的名字、地址编号、出生日期和图像,试着选择正确的数据类型。构建一个自己的 vcard 并打印它的内容。
|
||||
定义结构体 `Address` 和 `VCard`,后者包含一个人的名字、地址编号、出生日期和图像,试着选择正确的数据类型。构建一个自己的 `vcard` 并打印它的内容。
|
||||
|
||||
提示:
|
||||
VCard 必须包含住址,它应该以值类型还是以指针类型放在 VCard 中呢?
|
||||
第二种会好点,因为它占用内存少。包含一个名字和两个指向地址的指针的 Address 结构体可以使用 %v 打印:
|
||||
{Kersschot 0x126d2b80 0x126d2be0}
|
||||
|
||||
**练习 10.2** personex1.go:
|
||||
**练习 10.2** [personex1.go](exercises\chapter_10\personex1.go):
|
||||
|
||||
修改 personex1.go,使它的参数 upPerson 不是一个指针,解释下二者的区别。
|
||||
修改 personex1.go,使它的参数 `upPerson` 不是一个指针,解释下二者的区别。
|
||||
|
||||
**练习 10.3** point.go:
|
||||
**练习 10.3** [point.go](exercises\chapter_10\point.go):
|
||||
|
||||
使用坐标 X、Y 定义一个二维 Point 结构体。同样地,对一个三维点使用它的极坐标定义一个 Polar 结构体。实现一个 `Abs()` 方法来计算一个 Point 表示的向量的长度,实现一个 `Scale` 方法,它将点的坐标乘以一个尺度因子(提示:使用 `math` 包里的 `Sqrt` 函数)(function Scale that multiplies the coordinates of a point with a scale
|
||||
factor)。
|
||||
使用坐标 `X`、`Y` 定义一个二维 `Point` 结构体。同样地,对一个三维点使用它的极坐标定义一个 `Polar` 结构体。实现一个 `Abs()` 方法来计算一个 `Point` 表示的向量的长度,实现一个 `Scale()` 方法,它将点的坐标乘以一个尺度因子(提示:使用 `math` 包里的 `Sqrt()` 函数)(function Scale that multiplies the coordinates of a point with a scale factor)。
|
||||
|
||||
**练习 10.4** rectangle.go:
|
||||
**练习 10.4** [rectangle.go](exercises\chapter_10\rectangle.go):
|
||||
|
||||
定义一个 Rectangle 结构体,它的长和宽是 int 类型,并定义方法 `Area()` 和 `Perimeter()`,然后进行测试。
|
||||
定义一个 `Rectangle` 结构体,它的长和宽是 `int` 类型,并定义方法 `Area()` 和 `Perimeter()`,然后进行测试。
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[结构(struct)与方法(method)](10.0.md)
|
||||
- 上一节:[结构 (struct) 与方法 (method)](10.0.md)
|
||||
- 下一节:[使用工厂方法创建结构体](10.2.md)
|
||||
|
Reference in New Issue
Block a user