结构体
Go语言通过自定义的形式形成新的类型,结构体是类型中带有成员的复合类型。Go使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性。
Go语言中的类型可以被实例化,使用new或者“&”构造的类型实例的类型是类型的指针。
结构体成员由一系列的成员变量构成,这些成员变量也称为“字段”。字段有以下特性:
- 字段拥有自己的类型和值
- 字段名必须唯一
- 字段的类型也可以是结构体,甚至是字段所在结构体的类型。
6.1 定义结构体
Go语言的关键字type可以将各种基本类型定义为自定义类型,基本类型包括整型、字符串、布尔等。结构体是一种复合的基本类型,通过type定义为自定义类型,使结构体更便于使用。
结构体定义格式如下:
type 类型名 struct {
字段1 类型1
字段2 类型2
...
}
- 类型名:标识自定义结构体的名称,在同一包内不能重复。
- struct{}:表示结构体类型
- 字段1,字段2:表示结构体字段名,必须唯一。
- 类型1,类型2:表示字段的类型
结构体定义,举例如下:
type Point struct {
X int
Y int
}
// 同类型的变量可写一行
type Color struct {
R,G,B byte
}
6.2 实例化结构体—为结构体分配内存并初始化
结构体定义只是一种内存布局描述,只有当结构体实例化,才会真正的分配内存。因此必须在定义结构体并实例化后才能使用结构体的字段。
实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的内存是完全独立的。
Go 语言可以通过多种方式实例化结构体,根据实际需要可以选用不同的写法。
6.2.1 基本的实例化形式
结构体本身是一种类型,可以用var的方式声明结构体,即可完成实例化,形式如下:
var ins T
用结构体表示的点结构(Point)的实例化过程参见下面的代码:
type Point struct {
X int
Y int
}
var p Point
p.X = 1
p.Y = 2
在例子中,使用**“.”来访问结构体的成员变量**,如 p.X p.Y 等。结构体成员变量的赋值方法与普通变量一致。
6.2.2 创建指针类型的结构体
Go中可以使用new关键字对类型(整型、浮点、字符串、结构体等)进行实例化,结构体在实例化后形成指针类型的结构体。使用的格式如下:
ins := new(T)
Go 语言让我们可以像访问普通结构体一样使用“.”访问结构体指针的成员。
下面的例子定义了一个玩家( Player )的结构,玩家有名字、生命值,实例化玩家(Player )结构体后,可对成员进行赋值,代码如下:
type Player struct {
Name string
Hp int
}
p := new(Player)
p.Name = "zac"
p.Hp = 100
*提示:Go语言为了方便开发者访问结构体指针的成员交量,使用了语法糖( Syntactic sugar )技术,将 ins.Name 形式转换为(ins).Name。
6.2.3 取结构体地址实例化
Go 语言中,对结构体进行“&”取地址操作时,视为该类型一次new 的实例化 。取地址格式如下:
ins := &T{}
取地址实例化是最广泛的 种结构体实例化方式。可以使用函数封装初始化过程,代码如下:
type Cmd struct {
Name string
Var *int
}
func newCmd(name string, var *int) *Cmd {
return &Cmd{
Name: name,
Var : var,
}
}
var version int = 1
cmd = newCmd("version", &version)
6.3 初始化结构体成员的变量
结构体在实例化时可直接对成员变量进行初始化。有两种形式:一种是“键值对”形式,另一种是多种值按序的排列。
6.3.1 使用“键值对”初始化
初始化书写格式:
ins := 结构体类型名{
字段1:值1
字段2:值2
}
键值之间以":"分隔;键值对之间用“,”分隔
6.3.2 使用多个值的列表初始化
多个值用逗号分隔初始化的结构体,例如:
ins := 结构体类型名{
字段1的值,
字段2的值
}
使用这种格式时,必须注意:
- 必须初始所有字段
- 值的顺序必须与字段的顺序一致
- 键值对和值列表不能混用
6.3.3 初始化匿名结构体
匿名结构体没有类型名称,无需通过type关键字定义就可以直接使用。
匿名结构体的初始化写法由结构体定义和键值对初始化两部分组成。结构体定义时没有结构体类型名,只有字段和类型定义。键值对初始化部分由可选的多个键值对组成,如下格式所示:
ins := struct {
字段1 类型1
字段2 类型2
...
}{
初始化字段1: 字段1的值,
初始化字段2: 字段2的值,
...
}
键值对初始化部分是可选的,不初始化成员时{}为空。
6.4 构造函数
Go 语言 类型或结构体没有构造函数的功能。结构体的初始的过程可以使用函数封装实现。
6.4.1 多种方式创建和初始化结构体
如果使用结构体描述猫的特性,那么根据猫的颜色和名字可以有不同种类的猫。参考如下代码:
type Cat struct {
Color string
Name string
}
func NewCatByName(name string) *Cat {
return &Cat{
Name: name,
}
}
func NewCatByColor(color string) *Cat {
return &Cat{
Color: color,
}
}
由于Go语言中没有函数重载,为了避免函数名字冲突,使用 NewCatByName()和 NewCatByColor()两个不同的函数名表示不同的Cat构造过程。
6.4.2 带有父子关系的结构体的构造和初始化
黑猫是一种猫,猫是黑猫的一种泛称。同时描述这两种概念时,就是派生,黑猫派生自猫的种类。使用结构体描述猫和黑猫关系时, 将猫 Cat 的结构体嵌入 黑猫(BlackCat)中,表示黑猫拥有猫的特性,然后再使用两个不同的构造函数分别构造出黑猫和猫的两个结构体实例,参考如下代码:
type Cat struct {
Color string
Name string
}
type BlackCat struct {
Cat //嵌入Cat,类似于派生
}
// "构造基类"
func NewCat(name string) *Cat {
return &Cat{
Name: name,
}
}
// "构造子类"
func NewBlackCat(color string) *BlackCat {
cat := &BlackCat{}
cat.Color = color
return cat
}
这个例子中, Cat 结构体类似于面向对象中的“基类”。 BlackCat 嵌入 Cat 结构体,类似于面向对象中的“派 。实例化时, BlackCat 中的 Cat 会一并被实例化
总之, Go 语言中没有提供构造函数相关的特殊机制,用户根据自己的需求,将参数使用函数传递到结构体构造参数中即可完成构造函数的任务。