说明
Go语言是一种静态类型的编程语言。正因为如此,编译器就需要在进行编译时知道程序当中每个值的类型。当知道了这些类型信息,编译器就可以合理的使用值。这样的一种形式能够减少潜在的内存异常和bug,同时使编译器有机会对代码进行一定的性能优化,提高执行效率。
在go语言当中允许用户进行自定义类型。当用户新创建一个类型时,这个声明就给了编译器提供了一个框架,通过这个框架将必要的内存大小和表示信息告诉编译器。
Go语言里声明用户定义的类型有两种方法。最常用的方法是使用关键字struct,它可以让用户创建一个结构类型。而另外一种是之前说过的通过type自定义类型。
通过结构体,可以创建带有成员的复合类型。
使用
结构体成员是由一系列的成员变量构成,这些成员变量也被称为“字段”。字段有以下特性:
- 字段拥有自己的类型和值。
- 字段名必须唯一。
- 字段的类型也可以是结构体,甚至是字段所在结构体的类型。
例如:
// user: 在程序中定义一个用户类型
type user struct {
name string
age int
email string
}
在上面的代码中,定义了一个结构体,并且在结构体当中设置了name、age和email字段。通过代码可以看到,每个字段都是一个已知类型,当然也可以是用户定义的其他类型。
上面定义了一个结构体,下面就可以使用这个结构体来进行声明变量。
var zhangsan user
在上面的代码中创建了一个类型为user 且 值为zhangsan的一个变量。 当声明变量时,这个变量对应的值总是会被初始化。这个值要么用指定的值初始化,要么用零值(即变量类型的默认值)做初始化。对数值类型来说,零值是0;对字符串来说,零值是空字符串;对布尔类型,零值是false。对这个例子里的结构,结构里每个字段都会用零值初始化。
任何时候,创建一个变量并初始化为其零值,习惯是使用关键字var。这种用法是为了更明确地表示一个变量被设置为零值。
上面创建了一个变量zhangsan,如果想要对其初始化,可以采用下面的形式:
var zhangsan user
zhangsan.name = "张三"
zhangsan.age = 30
zhangsan.email = "zhangsan@qq.com"
如果变量被初始化为某个非零值,就配合结构字面量和短变量声明操作符来创建变量。
// 声明lisi这个变量并且设置为user类型同时进行初始化操作
lisi := user {
name: "李四",
email: "lisi@qq.com",
age: 30
}
上面的代码中,通过字面量和短变量操作符的方式声明了一个user类型的变量lisi并且进行了初始化。
结构体的初始化除了上面的两种形式以外,还可以采用下面的这种形式,按照结构体当中字段的顺序来进行赋值初始化。
zhangxiaoxiao := user{"张晓晓",30,"zhangxiaoxiao@qq.com"}
每个值也可以分别占一行,不过习惯上这种形式会写在一行里,结尾不需要逗号。这种形式下,值的顺序很重要,必须要和结构声明中字段的顺序一致。当声明结构类型时,字段的类型并不限制在内置类型,也可以使用其他用户定义的类型。
// admin 需要一个user类型作为管理者并且附加权限
type admin struct {
person user
level string
}
在上面的代码中,展示了一个名为admin的新结构类型。这个结构类型有一个名为person的user类型的字段,还声明了一个名为level的string字段。当创建具有person这种字段的结构类型的变量时,初始化用的结构字面量会有一些变化。
root := admin {
person: user {
name : "root",
age : 30 ,
email: "root@qq.com"
},
level: "super",
}
在上面的代码中,创建了一个admin类型的变量结构体root,在上面初始化的过程中,分别初始化了user结构体和level这个变量。
为结构体分配内存并初始化
上面其实我们已经说过go的结构体初始化,但是在go中对结构体进行初始化除了上述的方法以外,还可以使用new函数分配内存后再进行初始化。
具体写法如下:
ins := new(T)
其中:
- T 为类型,可以是结构体、整型、字符串等。
- ins:T 类型被实例化后保存到 ins 变量中,ins 的类型为 *T,属于指针。
需要知道的是,在通过new分配内存后,结构体在实例化后会形成指针类型的结构体。
下面的例子定义了一个玩家(Player)的结构,玩家拥有名字、生命值和魔法值,实例化玩家(Player)结构体后,可对成员进行赋值,代码如下:
type Player struct{
Name string
HealthPoint int
MagicPoint int
}
tank := new(Player)
tank.Name = "Canon"
tank.HealthPoint = 300
经过 new 实例化的结构体实例在成员赋值上与基本实例化的写法一致。
通过取结构体地址实例化
在Go语言中,对结构体进行&取地址操作时,视为对该类型进行一次 new 的实例化操作,取地址格式如下:
ins := &T{}
其中:
- T 表示结构体类型。
- ins 为结构体的实例,类型为 *T,是指针类型。
下面使用结构体定义一个命令行指令(Command),指令中包含名称、变量关联和注释等,对 Command 进行指针地址的实例化,并完成赋值过程,代码如下:
type Command struct {
Name string // 指令名称
Var *int // 指令绑定的变量
Comment string // 指令的注释
}
var version int = 1
cmd := &Command{}
cmd.Name = "version"
cmd.Var = &version
cmd.Comment = "show version"
取地址实例化是最广泛的一种结构体实例化方式,可以使用函数封装上面的初始化过程,代码如下:
func newCommand(name string, varref *int, comment string) *Command {
return &Command{
Name: name,
Var: varref,
Comment: comment,
}
}
cmd := newCommand(
"version",
&version,
"show version",
)