「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」。
结构体
概念
- 结构体(struct)就是一组字段(field)
- Go 语言中数组需要存储同一类型的数据,但在结构体中可以为不同字段定义不同的数据类型
- 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合
语法格式
声明结构体的语法格式
type T struct {
field1 type1
field2 type2
...
}
简洁版声明
多个字段类型相同时可以这样声明
type T struct {
a, b int
}
声明结构体变量
variable_name := T{value1, value2...}
声明结构体变量的几个🌰
package main
import "fmt"
type Vertex struct {
a int
b int
}
func main() {
var (
// 不指定字段,按顺序赋值
v1 = Vertex{1, 2}
// 指定字段
v2 = Vertex{a: 3}
// 不传值,默认取 字段类型的默认值
v3 = Vertex{}
// 指向结构体的指针
p1 = &Vertex{4, 5}
)
fmt.Println(v1, v2, v3, *p1)
}
运行结果
{1 2} {3 0} {0 0} {4 5}
访问字段
- 使用点号
.来访问字段 - 通过结构体指针来访问字段
使用点号.来访问字段
package main
import "fmt"
type Vertex struct {
a int
b int
}
func main() {
vertex := Vertex{
a: 11,
b: 22,
}
fmt.Println(vertex.a, vertex.b)
}
运行结果
11 22
访问字段的🌰
package main
import "fmt"
type Vertex struct {
a int
b int
}
func main() {
vertex := Vertex{
a: 11,
b: 22,
}
fmt.Println(vertex)
vertex.a = 1
vertex.b = 2
fmt.Println(vertex)
fmt.Println(vertex.a, vertex.b)
}
运行结果
{11 22}
{1 2}
1 2
结构体指针
声明结构体指针的几种方式
package main
import "fmt"
type Vertex struct {
a int
b int
}
func main() {
// 空指针
var vv1 *Vertex
vv2 := &Vertex{
1, 2,
}
fmt.Println(vv1, vv2)
}
运行结果
<nil> &{1 2}
通过结构体指针来访问字段
如果有一个指向结构体的指针 p,那么可以通过(*p).X来访问其字段 X,但这样太啰嗦了,Go 允许使用隐式间接引用,直接写p.X就可以了
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
// p 是指针
p := &v
// 通过指针访问字段,修改值
p.X = 999
fmt.Println(v)
}
运行结果
{999 2}
结构体的匿名字段
- 结构体可以包含一个或多个匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型就是字段的名字
- 匿名字段本身可以是一个结构体类型,就是结构体可以内嵌结构体,也可以是所有的内置类型和自定义类型
类比继承
- 可以粗略地将这个和面向对象语言中的继承概念相比较
- Go 语言中的继承是通过内嵌或组合来实现的,所以可以说,在 Go 语言中,相比较于继承,组合更受青睐
直接访问匿名字段的🌰
package main
import "fmt"
type inner struct {
age int
money int
}
type outer struct {
name string
others float64
// 匿名字段
int
inner
}
func main() {
o1 := outer{}
o1.age = 24
o1.money = 55
o1.name = "小菠萝"
o1.others = 1.1
o1.int = 60
fmt.Printf("o1.age is: %d\n", o1.age)
fmt.Printf("o1.money is: %d\n", o1.money)
fmt.Printf("o1.name is: %s\n", o1.name)
fmt.Printf("o1.others is: %f\n", o1.others)
fmt.Printf("o1.int is: %d\n", o1.int)
}
运行结果
o1.age is: 24
o1.money is: 55
o1.name is: 小菠萝
o1.others is: 1.100000
o1.int is: 60
声明一个有匿名字段的结构体的变量
package main
import "fmt"
type inner struct {
age int
money int
}
type outer struct {
name string
others float64
// 匿名字段
int
inner
}
func main() {
// 声明一个变量,指定结构体里面的值,嵌套结构体
o1 := outer{
"小菠萝", 1.1, 60, inner{
24, 55,
},
}
fmt.Println("o1 is:", o1)
}
运行结果
o1 is: {小菠萝 1.1 60 {24 55}}
命名冲突
- 当两个字段拥有相同的名字(可能是继承来的名字)时该怎么办呢?
- 外层名字会覆盖内层名字(但是两者的内存空间都保留),这提供了一种重载字段或方法的方式;
- 如果相同的名字在同一级别出现了两次,如果这个名字被使用了,将会引发一个错误(不使用没关系)
同一级别出现两次报错的🌰
package main
func main() {
type A struct{ a int }
type B struct{ a, b int }
type C struct {
// A、B 属于同一级
A
B
}
var c C
// A、B 都有 a 所以报错了
c.a = 123
c.b = 456
}
运行结果
./prog.go:12:3: ambiguous selector c.a
歧义选择器c.a,到底是c.A.a还是c.B.a呢?
不报错的🌰
package main
import "fmt"
func main() {
type A struct{ a int }
type B struct{ a, b int }
type C struct {
a int
A
B
}
var c C
c.a = 123
c.b = 456
fmt.Println(c)
}
运行结果
{123 {0} {0 456}}
A、B 两个结构体的 a 字段都是取的零值
如果想设置 A、B 里面的 a 字段值呢?
package main
import "fmt"
func main() {
type A struct{ a int }
type B struct{ a, b int }
type C struct {
a int
A
B
}
var c C
c.a = 123
c.b = 456
// 这样就能访问嵌套结构体的同名字段了
c.A.a = 999
c.B.a = 666
fmt.Println(c)
}
运行结果
{123 {999} {666 456}}